我正在阅读OCMock 参考文献,我对这两个OCMArg方法invokeBlockWithArgs感到困惑(第2.6节)
模拟对象将调用作为参数传递给存根方法的块。如果块接受参数并使用invokeBlock,则使用参数类型的默认值,例如,数值类型为零。使用invokeBlockWithArgs:可以指定调用块的参数;非对象参数必须包装在值对象中,表达式必须包装在圆括号中。
及checkWithBlock (第4.3条)
对于checkWithSelector:onObject:,当模拟对象接收到someMethod:时,它在anObject上调用aSelector。如果该方法采用参数,则模拟将传递传递给someMethod:的参数。该方法应该返回一个布尔值,指示参数是否与期望匹配。
因此,使用checkWithBlock,我们可以使用我们提供的任何参数调用已传递的块,使用invokeBlockWithArgs,似乎也可以这样做。那么,我应该在什么时候使用第一种或第二种方法?
发布于 2020-08-05 20:25:49
checkWithBlock
-您提供了一个将被调用的块来断言传递给存根方法的值,您与[OCMArg checkWithBlock:]
交换的值符合您的期望。
invokeBlockWithArgs
--这可以在固执块参数时使用,用样例参数调用它们。如果要检查块的行为,则需要这样做。
让我们想象一下,我们有一个简单的Networking
客户机,只有一个方法:
- (void)call:(NSURL *)url completion: (^void(NSData *, NSError *));
我们还有一些ModelClass
,它将Networking
的一个实例作为init
中的一个依赖项,如下所示:
@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
方法:
@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
作为newData
,nil
(从[NSNull null]
)调用为newError
。
https://stackoverflow.com/questions/63271115
复制相似问题