ios - OCMock:invokeBlockWithArgs 与 checkWithBlock
问题描述
我正在阅读 OCMock参考资料,我对这两个 OCMArg 方法 invokeBlockWithArgs 感到困惑(第 2.6 节)
模拟对象将调用作为参数传递给存根方法的块。如果块接受参数并使用invokeBlock,则使用参数类型的默认值,例如数字类型为零。使用 invokeBlockWithArgs:可以指定调用块的参数;非对象参数必须包裹在值对象中,表达式必须包裹在圆括号中。
和 checkWithBlock(第 4.3 节)
对于 checkWithSelector:onObject:,当 mock 对象接收到 someMethod: 时,它会在 anObject 上调用 aSelector。如果该方法接受一个参数,则模拟将传递传递给 someMethod: 的参数。该方法应返回一个布尔值,指示参数是否符合预期。
因此,使用 checkWithBlock 我们可以使用我们提供的任何参数调用传递的块,并且使用 invokeBlockWithArgs 似乎也可以这样做。那么什么时候应该使用第一种或第二种方法呢?
解决方案
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
采用var_args
需要终止的参数列表nil
来指示结束。我们需要使用[NSNull null]
来指示我们希望nil
作为某个参数传递给我们的完成块 - 所以在上面的示例中,我们的完成块将被调用expectedData
asnewData
和nil
(from [NSNull null]
) as newError
。
推荐阅读
- java - 如何在 Kotlin 中通过 Android 应用发布文本和图像?
- angular - Angular 8 中的可观察对象
- c++ - C++中的循环模板
- c# - 在 Visual Studio 2008 中找不到类型或命名空间,但.dll 明确包含在项目中,C#
- google-geocoding-api - 地理编码 - API - OVER_QUERY_LIMIT
- node.js - NodeJS/V8 中的方法入口和出口是否可以通过调试 API 进行跟踪?
- c# - ConcurrentBag 跳过一些项目 C#
- r - na.locf 对 177 个组使用 group_by
- javascript - 在 vue 中从纯 HTML 执行脚本
- vue.js - 在 axios Post 之后 router.push , Vuejs