代码之家  ›  专栏  ›  技术社区  ›  Greg

OCMock:为什么在尝试调用UIWebView mock时会出现无法识别的选择器异常?

  •  1
  • Greg  · 技术社区  · 14 年前

    编辑: 这都是由我的其他链接标志设置中的一个错误引起的。见 my answer below 更多信息。


    Test Case '-[FirstLookViewControllerTests testViewDidLoad]' started.
    2010-11-11 07:32:02.272 Unit Test[38367:903] -[NSInvocation getArgumentAtIndexAsObject:]: unrecognized selector sent to instance 0x6869ea0
    2010-11-11 07:32:02.277 Unit Test[38367:903] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation getArgumentAtIndexAsObject:]: unrecognized selector sent to instance 0x6869ea0'
    *** Call stack at first throw:
    (
        0   CoreFoundation                      0x010cebe9 __exceptionPreprocess + 185
        1   libobjc.A.dylib                     0x012235c2 objc_exception_throw + 47
        2   CoreFoundation                      0x010d06fb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
        3   CoreFoundation                      0x01040366 ___forwarding___ + 966
        4   CoreFoundation                      0x0103ff22 _CF_forwarding_prep_0 + 50
        5   Unit Test                           0x0000b29f -[OCMockRecorder matchesInvocation:] + 216
        6   Unit Test                           0x0000c1c1 -[OCMockObject handleInvocation:] + 111
        7   Unit Test                           0x0000c12a -[OCMockObject forwardInvocation:] + 43
        8   CoreFoundation                      0x01040404 ___forwarding___ + 1124
        9   CoreFoundation                      0x0103ff22 _CF_forwarding_prep_0 + 50
        10  Unit Test                           0x0000272a -[MyViewController viewDidLoad] + 100
        11  Unit Test                           0x0000926c -[MyViewControllerTests testViewDidLoad] + 243
        12  Unit Test                           0x0000537f -[SenTestCase invokeTest] + 163
        13  Unit Test                           0x000058a4 -[GTMTestCase invokeTest] + 146
        14  Unit Test                           0x0000501c -[SenTestCase performTest] + 37
        15  Unit Test                           0x000040c9 -[GTMIPhoneUnitTestDelegate runTests] + 1413
        16  Unit Test                           0x00003a87 -[GTMIPhoneUnitTestDelegate applicationDidFinishLaunching:] + 197
        17  UIKit                               0x00309253 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1252
        18  UIKit                               0x0030b55e -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 439
        19  UIKit                               0x0030aef0 -[UIApplication _run] + 452
        20  UIKit                               0x0031742e UIApplicationMain + 1160
        21  Unit Test                           0x0000468c main + 104
        22  Unit Test                           0x000026bd start + 53
        23  ???                                 0x00000002 0x0 + 2
    )
    terminate called after throwing an instance of 'NSException'
    /Users/gjritter/src/google-toolbox-for-mac-read-only/UnitTesting/RunIPhoneUnitTest.sh: line 151: 38367 Abort trap              "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" -RegisterForSystemEvents
    

    我的测试代码是:

    - (void)testViewDidLoad {
        MyViewController *viewController = [[MyViewController alloc] init];
    
        id mockWebView = [OCMockObject mockForClass:[UIWebView class]];
        [[mockWebView expect] setDelegate:viewController];
    
        viewController.webView = mockWebView;
    
        [viewController viewDidLoad];
        [mockWebView verify];
        [mockWebView release];
    }
    

    我的视图控制器代码是:

    - (void)viewDidLoad {
        [super viewDidLoad];
        webView.delegate = self;
    }
    

    我确实发现,如果我改为使用:

    - (void)testViewDidLoad {
        MyViewController *viewController = [[MyViewController alloc] init];
    
        id mockWebView = [OCMockObject partialMockForObject:[[UIWebView alloc] init]];
        //[[mockWebView expect] setDelegate:viewController];
    
        viewController.webView = mockWebView;
    
        [viewController viewDidLoad];
        [mockWebView verify];
        [mockWebView release];
    }
    

    但是,一旦我添加了被注释掉的期望值,使用部分mock时就会返回错误。

    我还有其他的测试在同一个项目中成功地使用mock。

    有什么想法吗?OCMock是否支持对UIKit对象的模拟?

    编辑: 根据下面答案中的建议,我尝试了以下测试,但得到的错误相同:

    - (void)testViewDidLoadLoadsWebView {
        MyViewController *viewController = [[MyViewController alloc] init];
        UIWebView *webView = [[UIWebView alloc] init];
    
        // This test fails in the same fashion with or without the next line commented
        //viewController.view;
    
        id mockWebView = [OCMockObject partialMockForObject:webView];
        // When I comment out the following line, the test passes
        [[mockWebView expect] loadRequest:[OCMArg any]];
    
        viewController.webView = mockWebView;
    
        [viewController viewDidLoad];
        [mockWebView verify];
        [mockWebView release];
    }
    
    2 回复  |  直到 7 年前
        1
  •  4
  •   Adam Milligan    14 年前

    UIKit类是一种神秘的动物,我发现在它们身上乱搞会带来数小时的调试乐趣。也就是说,我发现只要有一点耐心,你就能成功。

    我注意到你的代码的第一件事是你的控制器没有在你的测试中加载它的视图。我通常确保在运行任何测试之前总是强制加载视图。当然,这意味着您不能为web视图的初始化编写期望值,但在这种情况下,您实际上不需要编写期望值。你可以这样做:

    - (void)testViewDidLoadSetsWebViewDelegateToSelf {
        MyViewController *viewController = [[MyViewController alloc] init];
        // Force the view to load
        viewController.view;
    
        assertThat(controller.webView.delegate, equalTo(controller));
    }
    

    也就是说,如果您确实希望随后模拟Web视图,我建议使用现有的Web视图的部分模拟:

    - (void)testWebViewDoesSomething {
        MyViewController *viewController = [[MyViewController alloc] init];
        // Force the view to load
        viewController.view;
    
        id mockWebView = [OCMockObject partialMockForObject:controller.webView];
        [[mockWebView expect] someMethod];
    
        [controller doWhatever];
    
        [mockWebView verify];
    }
    

    事实上,我发现最好总是对任何UIView子类使用部分mock。如果您创建了一个完整的UIView模拟,那么当您尝试执行与之相关的视图时(例如将其添加到superview),它几乎总是会混乱地爆炸。

        2
  •  3
  •   Greg    14 年前

    事实证明,这是一个由一个字符引起的问题,在你看了几十遍之后才注意到。

    this post 在OCMock论坛上,我为单元测试目标设置了其他链接器标志 -ObjC -forceload $(PROJECT_DIR)/Libraries/libOCMock.a . 这是错误的; -forceload 应该是 -force_load . 一旦我纠正了这个错误,我的测试就成功了。