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
как newData
и nil
(из [NSNull null]
) как newError
.