该文章使用的API是OCMock老版本的API,新版本也兼容老版本的API,译者在用到老版本的API处已经添加了对应的新版本(OCMock3)的API供读者参考。
  爱好者
  这篇文章假设读者都能熟悉使用Xcode5的测试框架XCTest,或者BBD测试工具Kiwi或其他的iOS测试框架
  什么是mock?差不多是纸老虎
  当我们写单元测试的时候,不可避免的要去尽可能少的实例化一些具体的组件来保持测试既短又快。而且保持单元的隔离。在现代的面向对象系统中,测试的组件很可能会有几个依赖的对象。我们用mock来替代实例化具体的依赖class。mock是在测试中的一个伪造的有预定义行为的具体对象的替身对象。被测试的组件不知道其中的差异!你的组件是在一个更大的系统中被设计的,你可以很有信心的用mock来测试你的组件。
  常见的mock使用案例
  stub方法
  我们用一个简单的例子来开始解释OCMock中一般的stub语法。
  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
  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语法可能看起来有点奇怪,但这是普遍的做法:
  ourMockObject stub] whatItShouldReturn ] method:
  OCMock3 新版本对应API
  OCMStub([ourMockObject method:]).andReturn()
  一个非常重要的说明:注意[OCMArg any]的用法。当指定一个带参数的方法时,方法被调用并且参数为指定参数的话,mock会返回andReturn:指定的值。[OCMArg any]方法告诉stub匹配所有的参数值。举个例子:
  [car goFaster:84 units:@"mph"];
  不会触发stub,因为后一个参数不匹配”kph”.
  类方法
  OCMock会在mock实例上没有找到相同名字的实例方法的时候去找同名的类方法。在名字相同的情况下(类方法和实例方法同名),用classMethod来指定类方法:
  [[[[jalopy stub] classMethod] andReturn:@"expired"] checkWarrany];
  在OCMock3中classMethod和instanceMethod的stub方式一样,例如:
  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:
  id mockThing = [OCMock mockForClass[Thing class]];
  OCMock3 新版本对应API
  id mockThing = OCMStrictClassMock([Thing class]);
  这是我所说的‘vanilla’ mock。‘vanilla’ mock当调用一个没有stub的方法的时候会抛出一个异常。这会得到一个单调的mock,且在mock的生命周期中每一个方法调用都要被stub掉。(更多信息请看下一节关于stub)
  如果你不想stub很多方法,用‘nice’ mock。‘nice’ mock非常有礼貌而且不会在一个没有stub掉的方法被调用的时候抛出异常。
  id niceMockThing = [OCMock niceMockForClass[Thing class]];
  OCMock3 新版本对应API
  id mockThing = OCMClassMock([Thing class]);
  后一个mock类型是‘partial’ mock。当一个没有stub掉的方法被调用了,这个方法会被转发到真实的对象上。这是对mock技术上的欺骗,但是非常有用,当有一些类不适合让自己很好的被stub。
  Thing *someThing = [Thing alloc] init];
  id aMock = [OCMockObject partialMockForObject:someThing]
  OCMock3 新版本对应API
  Thing *someThing = [Thing alloc] init];
  id aMock = OCMPartialMock(someThing);
  验证方法是否被调用
  验证方法是否被调用非常简单。这个可以用expect来完成拒绝和验证方法:
  id niceMockThing = [OCMock niceMockForClass[Thing class]];
  [[niceMockThing expect] greeting:@"hello"];
  // verify the method was called as expected
  [niceMocking verify];
  OCMock3 新版本对应API
  id niceMockThing = OCMClassMock([Thing class]);
  OCMVerify([niceMockThing greeting:@"hello"]);
  当被验证的方法没有被调用的时候会抛出异常。如果你用的是XCTest,那么请用XCTAss**NotThrow来包装验证调用。拒绝方法调用也是同样的道理,但是会再方法调用的时候抛出异常。像stub,selector和传递过去验证的参数必须匹配调用时候传递过去的参数。用[OCMArg any]可以简化我们的工作。