关于OCMock的一些事儿

下载地址:html

http://ocmock.org/download/ios

源码及示例下载地址:git

https://github.com/erikdoe/ocmockgithub

项目配置ocmock地址:数据库

http://ocmock.org/ios/网络

ocmock features:异步

http://ocmock.org/features/函数

什么是mock?差很少就是纸老虎
单元测试

当 咱们写单元测试的时候,不可避免的要去尽量少的实例化一些具体的组件来保持测试既短又快。并且保持单元的隔离。在现代的面向对象系统中,测试的组件很可 能会有几个依赖的对象。咱们用mock来替代实例化具体的依赖class。mock是在测试中的一个伪造的有预约义行为的具体对象的替身对象。被测试的组 件不知道其中的差别!你的组件是在一个更大的系统中被设计的,你能够颇有信心的用mock来测试你的组件。学习

常见的mock使用案例

stub方法

咱们用一个简单的例子来开始解释OCMock中通常的stub语法。

1
2
3
id jalopy = [OCMock mockForClass[Car class]];
  [[[jalopy stub] andReturn:@ "75kph" ] goFaster:[OCMArg any] units:@ "kph" ];
  // if returning a scalar value, andReturnValue: can be used

OCMock3 新版本对应API

1
2
3
id jalopy = OCMStrictClassMock([Car class]);
OCMStub([jalopy goFaster:[OCMArg any] units:@ "kph" ]).andReturn(@ "75kph" );
// if returning a scalar value, andReturnValue: can be used

这个简单的例子首先从Car类中mock出一个jalopy(老爷车),而后,stub掉goFaster:方法让它返回字符串@”75kph”。stub语法可能看起来有点奇怪,但这是广泛的作法:

1
ourMockObject stub] whatItShouldReturn ] method:

OCMock3 新版本对应API

1
OCMStub([ourMockObject method:]).andReturn()

一个很是重要的说明:注意[OCMArg any]的用法。当指定一个带参数的方法时,方法被调用而且参数为指定参数的话,mock会返回andReturn:指定的值。[OCMArg any]方法告诉stub匹配全部的参数值。举个例子:

1
[car goFaster:84 units:@ "mph" ];

不会触发stub,由于最后一个参数不匹配”kph”.

类方法

OCMock会在mock实例上没有找到相同名字的实例方法的时候去找同名的类方法。在名字相同的状况下(类方法和实例方法同名),用classMethod来指定类方法:

1
[[[[jalopy stub] classMethod] andReturn:@ "expired" ] checkWarrany];

在OCMock3中classMethod和instanceMethod的stub方式同样,例如:

1
2
3
4
id classMock = OCMClassMock([SomeClass class]);
OCMStub([classMock aClassMethod]).andReturn(@ "Test string" );
// result is @"Test string"
NSString *result = [SomeClass aClassMethod];

mock类型 – niceMock,partialMock
OCMock提供了几种不一样类型的mock,每一个都有他们特定的使用场景。

用这种方式来建立任意mock:

1
id mockThing = [OCMock mockForClass[Thing class]];

OCMock3 新版本对应API

1
id mockThing = OCMStrictClassMock([Thing class]);

这就是我所说的‘vanilla’ mock。‘vanilla’ mock当调用一个没有stub的方法的时候会抛出一个异常。这会获得一个单调的mock,且在mock的生命周期中每个方法调用都要被stub掉。(更多信息请看下一节关于stub)

若是你不想stub不少方法,用‘nice’ mock。‘nice’ mock很是有礼貌并且不会在一个没有stub掉的方法被调用的时候抛出异常。

1
id niceMockThing = [OCMock niceMockForClass[Thing class]];

OCMock3 新版本对应API

1
id mockThing = OCMClassMock([Thing class]);

最后一个mock类型是‘partial’ mock。当一个没有stub掉的方法被调用了,这个方法会被转发到真实的对象上。这是对mock技术上的欺骗,可是很是有用,当有一些类不适合让本身很好的被stub。

1
2
Thing *someThing = [Thing alloc] init];
id aMock = [OCMockObject partialMockForObject:someThing]

OCMock3 新版本对应API

1
2
Thing *someThing = [Thing alloc] init];
id aMock = OCMPartialMock(someThing);

验证方法是否被调用

验证方法是否被调用很是简单。这个能够用expect来完成拒绝和验证方法:

1
2
3
4
id niceMockThing = [OCMock niceMockForClass[Thing class]];
  [[niceMockThing expect] greeting:@ "hello" ];
  // verify the method was called as expected
  [niceMocking verify];

OCMock3 新版本对应API

1
2
id niceMockThing = OCMClassMock([Thing class]);
OCMVerify([niceMockThing greeting:@ "hello" ]);

当 被验证的方法没有被调用的时候会抛出异常。若是你用的是XCTest,那么请用XCTAssertNotThrow来包装验证调用。拒绝方法调用也是一样 的道理,可是会再方法调用的时候抛出异常。就像stub,selector和传递过去验证的参数必须匹配调用时候传递过去的参数。用[OCMArg any]能够简化咱们的工做。

处理block参数

OCMock也能够处理block回调参数。block回调一般用于网络代码,数据库代码,或者在任何异步操做中。在这个例子中,思考下下面的方法:

1
2
- (void)downloadWeatherDataForZip:(NSString *)zip
               callback:(void (^)(NSDictionary *response))callback;

在这个例子中,咱们有一个下载天气压缩数据的方法,而且把下载下来的dictionary代理到一个block的回调中。在测试中,咱们经过预约义的天气数据来测试回调处理。这也是明智的测试失败场景。你永远不会知道网络上会返回你什么东西!

1
2
3
4
5
6
7
8
9
10
// 1. stub using OCMock andDo: operator.
[[[groupModelMock stub] andDo:^(NSInvocation *invoke) {
         //2. declare a block with same signature
         void (^weatherStubResponse)(NSDictionary *dict);
         //3. link argument 3 with with our block callback
         [invoke getArgument:&weatherStubResponse atIndex:3];
         //4. invoke block with pre-defined input
         NSDictionary *testResponse = @{@ "high" : 43 , @ "low" : 12};
         weatherStubResponse(groupMemberMock);
     }]downloadWeatherDataForZip@ "80304"  callback:[OCMArg any] ];

OCMock3 新版本对应API

1
2
3
4
5
6
7
8
9
10
// 1. stub using OCMock andDo: operator.
OCMStub([groupModelMock downloadWeatherDataForZip:@ "80304"  callback:[OCMArg any]]]).andDo(^(NSInvocation *invocation){
         //2. declare a block with same signature
         void (^weatherStubResponse)(NSDictionary *dict);
         //3. link argument 3 with with our block callback
         [invoke getArgument:&weatherStubResponse atIndex:3];
         //4. invoke block with pre-defined input
         NSDictionary *testResponse = @{@ "high" : 43 , @ "low" : 12};
         weatherStubResponse(groupMemberMock);
     });

这里的大致思想至关简单,即使如此,他的实现也须要一些说明:

1. 这个mock对象使用带NSInvocation参数的“andDo”方法。一个NSInvocation对象表明一个 ‘objectivetified’(实在不知道这个什么鬼)表现的方法调用。经过这个NSinvocation对象,使得拦截传递给咱们的方法的 block参数变得可能。

2.用与咱们测试的方法中相同的方法签名声明一个block参数。

3.NSInvocation实 例方法"getArgument:atIndex:"将赋值后的块函数传递都原始函数中定义的块函数中。注意:在Objective-C中,传递给任意方 法的前两个参数都是“self”和“_cmd”.这是一个运行时的小功能以及用下标来获取NSInvocation参数时咱们须要考虑的东西。

4.最后,传递这个回调的预约义字典。

最后

但愿这篇文章和例子已经陈述清楚一些OCMock最通用的用法。OCMock站点:http://ocmock.org/features/是一个最好的学习OCMock的地方。mock是单调的可是对于一个现代的OO系统倒是必须的。若是一个依赖图很难用mock来测试,这个迹象代表你的设计须要从新考虑了。

转自:http://www.cocoachina.com/ios/20150508/11769.html

相关文章
相关标签/搜索