(三)验证方法的调用
调用方式:
OCMVerify([mock someMethod]);
OCMVerify(never(), [mock doStuff]); //从没被调用
OCMVerify(times(n), [mock doStuff]); //调用了N次
OCMVerify(atLeast(n), [mock doStuff]); //最少被调用了N次
OCMVerify(atMost(n), [mock doStuff]);
使用场景:
在单元测试中可以验证某个方法是否执行,以及执行了几次。
延时验证调用:
OCMVerifyAllWithDelay(mock, aDelay);
使用场景:该功能用于等待异步操作会比较多,其中aDelay为预期最长等待时间。
(四)添加预期
调用方式:
准备数据:
NSDictionary *info = @{@"name": @"momo"};
id mock = OCMClassMock([MOOCMockDemo class]);
添加预期:
OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg any]]);
可以预期不执行:
OCMReject([mock handleLoadFailWithPerson:[OCMArg any]]);
可以验证参数:
// 预期 + 参数验证
OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg checkWithBlock:^BOOL(id obj) {
MOPerson *person = (MOPerson *)obj;
return [person.name isEqualToString:@"momo"];
}]]);
可以预期执行顺序:
// 预期下列方法顺序执行
[mock setExpectationOrderMatters:YES];
OCMExpect([mock handleLoadSuccessWithPerson:[OCMArg any]]);
OCMExpect([mock showError:NO]);
可以忽略参数(预期方法执行时):
OCMExpect([mock showError:YES]).ignoringNonObjectArgs; // 忽视参数
执行:
[MOOCMockDemo handleLoadFinished:info];
断言:
OCMVerifyAll(mock);
可以延迟断言:
OCMVerifyAllWithDelay(mock, 1); // 支持延迟验证
最后的 OCMVerifyAll 会验证前面的期望是否有效,只要有一个没调用,就会出错。
(五)参数约束
调用方式:
OCMStub([mock someMethodWithAnArgument:[OCMArg any]])
OCMStub([mock someMethodWithPointerArgument:[OCMArg anyPointer]])
OCMStub([mock someMethodWithSelectorArgument:[OCMArg anySelector]])
使用场景:在使用 OCMVerify()方法验证某个方法是否调用是使用,单元测试会验证方法参数是否一致,如果不一致就是提示验证失败,此时如果只关注方法调用,并不关注参数即可使用[OCMArg any]传参。
(六)网络接口的模拟
顾名思义可以 mock 网络接口的数据返回,测试不同数据下代码的走向以及准确性。
调用方式:
id mockManager = OCMClassMock([JDStoreNetwork class]);
[orderListVc setComponentsNet:mockManager];
[OCMStub([mockManager startWithSetup:[OCMArg any] didFinish:[OCMArg any] didCancel:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
void (^successBlock)(id components,NSError *error) = nil;
[invocation getArgument:&successBlock atIndex:3];
successBlock(@{@"code":@"1",@"resultCode":@"1",@"value":@{@"showOrderSearch":@"NO"}},nil);
}];
以上就是在调用 setComponentsNet 方法内部调用了接口,该方法就可以在调用接口后模拟需要的返回数据,successBlock 中的就是返回的测试数据。本方式是通过获取接口调用的方法签名,获取 successBlock 成功回调传参并手动调用。同样可以模拟接口失败的情况,只需获取到签名中的对应的失败回调就可以实现了。
使用场景:书写单元测试方法时涉及网络接口的模拟,通过该方式 mock 接口返回结果。
(七)恢复类
置换类方法后,可以将类恢复到原来的状态,通过调用 stopMocking 来完成。
调用方式:
id classMock = OCMClassMock([SomeClass class]);
/* do stuff */
[classMock stopMocking];
使用场景:
正常对实例对象置换后,mock 对象释放后会自动调用 stopMocking,但是添加到类方法上的 mock 对象会跨越了多个测试,mock 的类对象在置换后不会 deallocated,需要手动来取消这个 mock 关系。
(八)观察者模拟-创建一个接受通知的实例
调用方式:
- (void)testPostNotification {
Person *person1 = [[Person alloc] init];
id observerMock = OCMObserverMock();
//给通知中心设置观察者
[[NSNotificationCenter defaultCenter] addMockObserver: observerMock name:@"name" object:nil];
//设置观察期望
[[observerMock expect] notificationWithName:@"name" object:[OCMArg any]]; //调用要验证的方法
[person1 methodWithPostNotification];
[[NSNotificationCenter defaultCenter] removeObserver:observerMock];
// 调用验证
OCMVerifyAll(observerMock);}
使用场景:
创建一个 mock 对象,可以用来观察通知。mock 必须注册以接收通知。
(九)mock协议
调用方式:
id protocolMock = OCMProtocolMock(@protocol(SomeProtocol));
/*严格的协议*/
id classMock = OCMStrictClassMock([SomeClass class]);
id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol));
id protocolMock = OCMProtocolMock(@protocol(SomeProtocol));
/*严格的协议*/
id classMock = OCMStrictClassMock([SomeClass class]);
id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol));
调用场景:当需要创建一个实例,让其具有协议的所定义的功能时使用。
03mock使用限制
对于同个方法,先stub后expect是不行的:因为先stub的话,所有的调用都会变成stub,这样子即使过程调用该方法,最后OCMVerifyAll验证也会失败;解决的办法是,在OCMExpect上顺便stub,比如:OCMExpect([mock someMethod]).andReturn(@"a string"),或者将stub置于expect之后。
部分模拟不适用于某些类:如NSString和NSDate,这些”toll-free bridged”的类,否则会抛出异常。
某些方法不能stub:如:init、class、methodSignatureForSelector、forwardInvocation这些。
NSString与NSArray的类方法不能stub,否则无效。
NSObject的方法调用不能验证,除非在子类中重写。
苹果核心类的私有方法调用不能被验证,如以_开头的方法。
延时验证方法调用不支持,暂时只支持期望-运行-验证模式的延时验证。
OCMock不支持多线程。
最后
希望这篇文章和例子已经陈述清楚了一些 OCMock 最通用的用法。OCMock 站点:http://ocmock.org/features/ 是一个最好的学习 OCMock 的地方。mock 是单调的但是对于一个应用程序却是必须的。如果一个方法很难用 mock 来测试,这个迹象表明你的设计需要重新考虑了。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理