【问题标题】:OCMock "expected method was not invoked" even though I'm calling it myself即使我自己调用 OCMock “未调用预期的方法”
【发布时间】:2013-06-25 12:40:40
【问题描述】:

我正在尝试在 iOS 项目中设置一个简单的 OCMock 单元测试,只是为了让自己熟悉框架。

我有一个模拟的DataLoader 类,即使我自己调用该方法,我的期望也失败了:

- (void)testSimpleMocking {
    // Mock the class
    id mock = [OCMockObject niceMockForClass:[DataLoader class]];

    // Override the 'dispatchLoadToAppDelegate:' to be a no-op
    [[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];

    // Expect the method to be called
    [[mock expect] dispatchLoadToAppDelegate:[OCMArg any]];

    // Call the method
    [mock dispatchLoadToAppDelegate:nil];

    // Verify
    [mock verify];
}

但是,当我运行此测试时,我收到错误:

/Users/Craig/projects/MyApp/Unknown.m: -[MockingDataLoaderTest testSimpleMocking] : OCMockObject[DataLoader]:
expected method was not invoked: dispatchLoadToAppDelegate:<OCMAnyConstraint: 0x1a3d890>

当我自己调用该方法时,这怎么可能?

编辑:更复杂的案例:

- (void)testDataLoaderWaitsForDownload {
    id mock = [OCMockObject niceMockForClass:[DataLoader class]];
    id metadataItem = [OCMockObject niceMockForClass:[NSMetadataItem class]];

    // Prepare NSMetadataItem
    [[[metadataItem expect] andReturn:nil] valueForAttribute:NSMetadataItemURLKey];

    // CODERUN
    [mock waitForDownload:metadataItem thenLoad:YES];

    //VERIFY
    [metadataItem verify];
}

以及waitForDownload:thenLoad:方法的实现:

- (void)waitForDownload:(NSMetadataItem *)file thenLoad:(BOOL)load {
    NSURL *metadataItemURL = [file valueForAttribute:NSMetadataItemURLKey];
    ...

因错误而失败:

Unknown.m:0: error: -[MockingDataLoaderTest testDataLoaderWaitsForDownload] : OCMockObject[NSMetadataItem]: expected method was not invoked: valueForAttribute:@"kMDItemURL"

【问题讨论】:

  • 在您的编辑中,您在模拟对象上调用waitForDownload,因此不会调用真正的方法。根据您的目标,您希望 DataLoader 成为真实对象或部分模拟。

标签: ios cocoa unit-testing mocking ocmock


【解决方案1】:

在您的测试中,stub 具有优先权,因为它首先被调用。如果您切换expectstub 的顺序,您的测试应该会通过。

同时使用expectstub 的原因(具有相同的参数期望)是为了确保至少发生一个调用,然后响应后续调用而不会失败。

如果您真的只需要一个方法调用,只需将 andReturn: 添加到 expect 子句...

- (void)test_dispatchLoadToAppDelegate_isCalledExactlyOnce {
    // Mock the class
    id mock = [OCMockObject niceMockForClass:[DataLoader class]];

    // Expect the method to be called
    [[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];

    // Call the method
    [mock dispatchLoadToAppDelegate:nil];

    // Verify
    [mock verify];
}

另一种情况:

- (void)test_dispatchLoadToAppDelegate_isCalledAtLeastOnce {
    // Mock the class
    id mock = [OCMockObject niceMockForClass:[DataLoader class]];

    // Expect the method to be called
    [[[mock expect] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];

    // Handle subsequent calls
    [[[mock stub] andReturn:nil] dispatchLoadToAppDelegate:[OCMArg any]];

    // Call the method
    [mock dispatchLoadToAppDelegate:nil];

    // Call the method (again for fun!)
    [mock dispatchLoadToAppDelegate:nil];

    // Verify
    [mock verify];
}

对于这种特殊情况,您似乎可以使用niceMockForClass,但如果您希望存根返回非零值,那么您必须以任何方式调用stub

【讨论】:

  • 我会添加 andReturn: 调用期望...我知道期望和存根是不同的东西,但对我来说它看起来不那么多余。
  • 感谢 Ben 的回复 - 这更有意义,但我现在遇到了一个问题,即再次手动调用的期望(不是存根),但没有达到期望。 (我在我的问题中添加了新情况。)
【解决方案2】:

Ben Flynn 是正确的,颠倒 stubexpect 的顺序应该会让你的测试通过,但我会更进一步,建议你应该删除 stub 调用。您编写此测试的方式表明stubexpect 的先决条件,但事实并非如此。

expect 表示该方法必须调用一次且仅一次(对于每个期望)。 stub 表示该方法可能被调用零次或多次。通常你expect 调用对测试很重要,stub 是副作用。或者使用一个漂亮的模拟,只为重要的调用设置期望。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多