iOS 单元测试之常用框架 OCMock 详解(2)

发表于:2022-8-19 09:41

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:王中文    来源:京东零售技术

  (三)验证方法的调用
  调用方式:
  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),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号