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

时间:2016-02-24 16:56:06

标签: 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(firstInvocation).to.equal(NO);

失败

这里发生了什么?

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

1 个答案:

答案 0 :(得分:3)

我终于得到了第一个工作的解决方案。问题是如果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块中的XCTFail:你需要包含它,否则你的测试会是绿色的,尽管发生了异常。