首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >OCMock: invokeBlockWithArgs对checkWithBlock

OCMock: invokeBlockWithArgs对checkWithBlock
EN

Stack Overflow用户
提问于 2020-08-05 18:08:41
回答 1查看 518关注 0票数 0

我正在阅读OCMock 参考文献,我对这两个OCMArg方法invokeBlockWithArgs感到困惑(第2.6节)

模拟对象将调用作为参数传递给存根方法的块。如果块接受参数并使用invokeBlock,则使用参数类型的默认值,例如,数值类型为零。使用invokeBlockWithArgs:可以指定调用块的参数;非对象参数必须包装在值对象中,表达式必须包装在圆括号中。

及checkWithBlock (第4.3条)

对于checkWithSelector:onObject:,当模拟对象接收到someMethod:时,它在anObject上调用aSelector。如果该方法采用参数,则模拟将传递传递给someMethod:的参数。该方法应该返回一个布尔值,指示参数是否与期望匹配。

因此,使用checkWithBlock,我们可以使用我们提供的任何参数调用已传递的块,使用invokeBlockWithArgs,似乎也可以这样做。那么,我应该在什么时候使用第一种或第二种方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-05 20:25:49

checkWithBlock -您提供了一个将被调用的块来断言传递给存根方法的值,您与[OCMArg checkWithBlock:]交换的值符合您的期望。

invokeBlockWithArgs --这可以在固执块参数时使用,用样例参数调用它们。如果要检查块的行为,则需要这样做。

让我们想象一下,我们有一个简单的Networking客户机,只有一个方法:

代码语言:javascript
运行
复制
- (void)call:(NSURL *)url completion: (^void(NSData *, NSError *));

我们还有一些ModelClass,它将Networking的一个实例作为init中的一个依赖项,如下所示:

代码语言:javascript
运行
复制
@property (nonatomic, nullable, strong) NSData *lastData;
@property (nonatomic, nullable, strong) NSError *lastError;

@property (nonatomic, strong) Networking *networking;

- (instancetype)initWith:(Networking *)networking { /* */ }

- (void)getData {
    [self.networking call:[NSURL URLWithString:@"www.stackoverflow.com"]
               completion: ^(NSData *newData, NSError *newError) {
                             self.lastData = newData;
                             self.lastError = newError;
               }];
}

然后,我们可以在我们的测试类中像这样测试getData方法:

代码语言:javascript
运行
复制
@property (nonatomic, strong) Networking *networkingMock;
@property (nonatomic, strong) ModelClass *model;

- (void)setUp {
    [super setUp];

    self.networkingMock = OCMClassMock([Networking class]);
    self.model = [[ModelClass alloc] initWith:self.networkingMock];
}

// Assert proper argument was passed by explicitly providing
// expected value in `OCMStub`/`OCMExpect` call
// OCMock will check that they are equal for us
- (void)test_getData_passesCorrectURL {

    // Arrange
    OCMExpect([self.networkingMock call:[NSURL URLWithString:@"www.stackoverflow.com"] 
                             completion:OCMOCK_ANY]);

    // Act
    [self.model getData];

    // Assert
    OCMVerifyAll(self.networkingMock);
}

// Assert proper argument is passed in a custom assertion block
// OCMock will call this block, passing the value so that we can inspect it
// We cannot use `invokeBlockWithArgs` to check the `url` parameter
// because its not a block.
- (void)test_getData_passesCorrectURL_withCheckWithBlock {
    // Arrange
    OCMExpect([self.networkingMock call:[OCMArg checkWithBlock:^BOOL(id value) {
                             // This is the custom assertion block, we can inspect the `value` here
                             // We need to return `YES`/`NO` depending if it matches our expectetations

                             if (![value isKindOfClass:[NSURL class]]) { return NO };
                             NSURL *valueAsURL = (NSURL *)value;
                             return [valueAsURL isEqualToURL:[NSURL URLWithString:@"www.stackoverflow.com"]];
                             }] 
                             completion:OCMOCK_ANY]);

    // Act
    [self.model getData];

    // Assert
    OCMVerifyAll(self.networkingMock);
}

// We want to assert the behavior of the completion block passed to the `Networking`
// in the `getData` method. So we need a way to invoke this block somehow - 
// in previous two tests it was never called, because `OCMock` replaces the 
// implementations of methods in stubbed classes.
- (void)test_getData_shouldSetLastData_onCompletion {

    // Arrange
    NSData *expectedData = [NSData data];
    OCMExpect([self.networkingMock call:OCMOCK_ANY
                             completion:[OCMArg invokeBlockWithArgs:expectedData, [NSNull null], nil]]);

    // Act
    [self.model getData];

    // Assert
    XCTAssertEqualObjects(self.model.lastData, expectedData);

}

在最后一个示例中,如果使用checkWithBlock而不是invokeBlockWithArgs,则不会调用在ModelClass中传递的完成块。相反,将调用自定义断言块(正如我们在第二次测试中看到的那样),并将指向完成块的指针作为值传递。

当然,您可以将这个指针转换为块类型,并使用一些参数调用块--但是这是额外的工作,可以通过invokeBlockWithArgs来避免。

注意:invokeBlockWithArgs接受需要用nil终止以指示结束的参数的var_args列表。我们需要使用[NSNull null]来表示我们希望将nil作为一个特定的参数传递给我们的完成块--所以在上面的示例中,我们的完成块将被调用,expectedData作为newDatanil (从[NSNull null])调用为newError

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63271115

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档