ios - 验证使用两个不同参数调用方法两次

标签 ios objective-c core-data ocmock

我想测试一个方法是否被调用两次,第一次为参数传递 YES,第二次为 NO。 让事情变得复杂的是,我想测试的方法是一个类方法,我不确定这是否与我看到的问题有关。

我的测试如下所示:

- (void)testCreatesMessagesWithCorrectHTMLForcing {
    id messageClassMock = OCMClassMock([MyMessage class]);
    [messageClassMock setExpectationOrderMatters:YES];
    [[[messageClassMock expect] andForwardToRealObject] messageForDictionary:[OCMArg any]
                                                          forceHTMLRendering:YES
                                                                   inContext:[OCMArg any]];
    [[[messageClassMock expect] andForwardToRealObject] messageForDictionary:[OCMArg any]
                                                          forceHTMLRendering:NO
                                                                   inContext:[OCMArg any]];
    NSMutableDictionary *mockJSON = [self.mockJSON mutableCopy];
    MyThread *classUnderTest = [MyThread threadForDictionary:mockJSON
                                                                       inContext:self.mockContext];
    OCMVerifyAll(messageClassMock);
    [messageClassMock stopMocking];
}

threadForDictionary:inContext: 方法为线程中的每条消息调用 messageForDictionary:forceHTMLRendering:inContext:,并且需要一个对象作为返回值。这就是我添加 andForwardToRealObject 的原因,否则我会因为返回值为 nil 而收到异常。正如您从签名中可以想象的那样,它是将 JSON 解析为 CoreData 对象。

添加此测试会使同一测试文件中的所有其他测试失败,并显示以下消息

unexpected method invoked: messageForDictionary:<OCMAnyConstraint: 0x7fab637063d0> forceHTMLRendering:NO inContext:<OCMAnyConstraint: 0x7fab63706eb0>
expected:   messageForDictionary:<OCMAnyConstraint: 0x7fab6371cb20> forceHTMLRendering:YES inContext:<OCMAnyConstraint: 0x7fab6371fb50>"

我不明白为什么会发生这种情况,因为我最后调用了 stopMocking 所以其他测试不应该受到影响。

以下更改使其他测试正确运行:

  • 删除两个期望中的任何一个。两者中的哪一个并不重要,只要只有一个即可。
  • 将该方法重命名为 testZ。这样,它就按字母顺序排列在同一文件中的其他测试之后;因此,最后执行并且似乎不再影响它们。

由于 setExpectationOrderMatters:YES 似乎不起作用,我尝试自己检查订单:

- (void)testCreatesMessagesWithCorrectHTMLForcing {
    id messageClassMock = OCMClassMock([MyMessage class]);
    __block BOOL firstInvocation = YES;
    [[[messageClassMock expect] andForwardToRealObject] messageForDictionary:[OCMArg any]
                                                          forceHTMLRendering:[OCMArg checkWithBlock:^BOOL (id obj) {
        NSNumber *boolNumber = obj;
        expect([boolNumber boolValue]).to.equal(firstInvocation);
        firstInvocation = NO;
        return YES;
    }]
                                                                   inContext:[OCMArg any]];
    NSMutableDictionary *mockJSON = [self.mockJSON mutableCopy];
    MyThread *classUnderTest = [MyThread threadForDictionary:mockJSON
                                                                       inContext:self.mockContext];
    expect(firstInvocation).to.equal(NO);
    OCMVerifyAll(messageClassMock);
    [messageClassMock stopMocking];
}

但是 checkWithBlock: 似乎没有被调用。 (测试失败于 expect(firstInitation).to.equal(NO);)

这是怎么回事?

是否有另一种(更好?)方法使用 OCMock 编写测试来检查是否以正确的顺序使用正确的参数调用该方法?

最佳答案

我终于找到了第一个可行的解决方案。问题是,如果 expectationOrderMattersYES,OCMock 会引发异常。由于异常,测试过早退出,并且从未调用 stopMocking ,这导致模拟无法正确清理。对模拟方法的后续调用会失败并出现相同的异常,从而导致所有测试失败。

解决方案是确保即使一切都出错了也会调用 stopMocking。我通过像这样使用 try-catch 实现了这一点(对宏的更改和使用 andReturn 而不是 andForwardToRealObject 并不重要):

MyMessage *message = [MyMessage insertInManagedObjectContext:self.mockContext];
id messageClassMock = OCMStrictClassMock([MyMessage class]);
@try {
    [messageClassMock setExpectationOrderMatters:YES];
    OCMExpect([messageClassMock messageForDictionary:[OCMArg any]
                                  forceHTMLRendering:NO
                                           inContext:[OCMArg any]]).andReturn(message);
    OCMExpect([messageClassMock messageForDictionary:[OCMArg any]
                                  forceHTMLRendering:YES
                                           inContext:[OCMArg any]]).andReturn(message);
    MyThread *classUnderTest = [MyThread threadForDictionary:self.mockJSON
                                                                       inContext:self.mockContext];
    OCMVerifyAll(messageClassMock);
}
@catch (NSException *exception) {
    XCTFail(@"An exception occured: %@", exception); // you need this, otherwise the test will incorrectly be green.
}
@finally {
    [messageClassMock stopMocking];
}

注意 catch block 中的 XCTFail:您需要包含它,否则尽管发生异常,您的测试仍将是绿色的。

关于ios - 验证使用两个不同参数调用方法两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35608314/

相关文章:

ios - 你应该把所有东西都存储在 CoreData 中吗?

ios - 核心数据 WAL 模式不会持久化对 .db 的更改,只有 .db-wal 和 .db-shm

ios - 将从 xib 加载的 View 添加到 UIScrollview 中

ios - 当我渲染印地语字体时,UICollectionView 滚动滞后

ios - Swift - iOS10 - CoreData - Today Widget 中的 FetchRequest 总是返回 0 个元素

ios - 代码 : How To Customize Text Field Font With My Own TTF Font?

iPhone NS时区 : localTimeZone confusion

iOS - NSManagedObject 在不保存的情况下保持分配多长时间

ios - XCode 7 如何固定到 super View 而不是播放指南

ios - 如何从第三个 viewController 返回到第一个 viewController