我的项目使用OCMock、OHHTTPStubs和XCTest。我尝试测试SDK(由我实现的SDK),所以我截取Http响应/请求,并添加一些对回调方法的期望。每个单元测试都有一些期望,即委托方法将被正确调用,在设置了所有期望之后,我对每个委托方法都包含了拒绝,以确保只调用指定的方法,而不调用其他方法。
我的单元测试示例:
// stub http
... here are some http stubs...
// expect
[[self.mockDelegate expect] didSomethigHappend:[OCMArg checkWithBlock:^BOOL(id obj) {
BOOL result = NO;
// testing parameter object
if(result) {
// call next method on SDK
[self.objectToTest nextMethod];
}
return result;
}] withError:[OCMArg isNil]];
// reject any other call:
[[self.mockDelegate reject] didSomethigHappend:[OCMArg any] withError:[OCMArg any]];
[[self.mockDelegate reject] dodSomethig2:[OCMArg any] withError:[OCMArg any]];
[[self.mockDelegate reject] dodSomethig3:[OCMArg any] withError:[OCMArg any]];
[super.objectToTest doSomethigWithDelegate:super.mockDelegate]; // run
[super.mockDelegate verifyWithDelay:3]; // verify
所有测试都成功通过,但运行所有测试都需要花费大量时间。但我看到的是,当我删除这些拒绝时,所有测试的运行速度都快了3倍。经过一些调试后,我检查了OCMock库方法的实现:
- (void)verifyWithDelay:(NSTimeInterval)delay atLocation:(OCMLocation *)location
{
NSTimeInterval step = 0.01;
while(delay > 0)
{
if([expectations count] == 0)
break;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:step]];
delay -= step;
step *= 2;
}
[self verifyAtLocation:location];
}
在注册拒绝的地方,"期望值"变量总是包含这些拒绝,因此它会等待所有延迟时间。
有人有同样的问题吗?也许我做错了什么,这是正确的行为?
OCMock有一个bug。
这里有一个方法swizzling的变通方法:
// stubbing verify with delay from ocmock framework
// because ocmock if has any registered rejects
// waits whole specified time, so we need to change this flow
// so it will wait for all expectation has occure and after that wait some steps to make sure that any reject has not invoked
static dispatch_once_t swizzle_token;
dispatch_once(&swizzle_token, ^{
SEL originalSelector = @selector(verifyWithDelay:);
SEL swizzledSelector = @selector(fake_verifyWithDelay:);
Method originalMethod = class_getInstanceMethod([OCMockObject class], originalSelector);
Method swizzledMethod = class_getInstanceMethod([VDFUsersServiceBaseTestCase class], swizzledSelector);
BOOL didAddMethod =
class_addMethod([OCMockObject class],
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod([OCMockObject class],
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
这里是fake_verify方法:
- (void)fake_verifyWithDelay:(NSTimeInterval)delay {
NSTimeInterval step = 0.1;
while (delay > 0) {
@try {
[self verify];
break;
}
@catch (NSException *e) {}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:step]];
delay -= step;
step += 0.1;
}
[self verify];
}