OCMock 3 参考

  • OCMock 3 参考
  • 1.创建Mock对象
    • 1.1 类Mock
    • 1.2 协议Mock
    • 1.3 严格的类和协议Mock
    • 1.4 部分Mock
    • 1.5 观察者Mock
  • 2 置换方法
    • 2.1 置换方法(待置换的方法返回objects)
    • 2.2 置换方法(待置换的方法返回values)
    • 2.3 委托到另一个方法(置换委托方法到另外一个方法)
    • 2.4 置换一个blcok方法.
    • 2.5 置换方法的参数
    • 2.6 调用某个方法就抛出异常
    • 2.7 调用某个方法就发送通知
    • 2.8 链式调用
    • 2.9 转发的原来的对象/类
    • 2.10 什么也不做
  • 3 验证作用
    • 3.1 运行后就验证
    • 3.2 置换后验证
  • 4 参数约束
    • 4.1 任意参数约束
    • 4.2 忽略非对象的参数
    • 4.3 匹配参数
    • 4.4 使用Hamcrest来匹配
  • 5 类方法的Mock
    • 5.1 置换类方法
    • 5.2 验证类方法的调用
    • 5.3 有歧义的类方法和实例方法
    • 5.4 恢复类
  • 6 部分Mock
    • 6.1 置换方法
    • 6.2 验证方法调用
    • 6.3 恢复对象
  • 7 严格mock和期望
    • 7.1 Expect-run-verify 期望-运行-验证
    • 7.2 严格的mock 和快速失败
    • 7.3 置换操作和预期
    • 7.4 延时验证
    • 7.5 依序验证
  • 8 观察者mock
    • 8.1 准备工作
    • 8.2 验证
  • 9 进阶话题
    • 9.1 对于普通的mock,快速失败
    • 9.2 在OCMVerifyAll时重新抛出异常
    • 9.3 置换创建对象的方法
    • 9.4 基于实例对象的方法替换
  • 10 使用限制
    • 10.1 在一个指定的类上,只能有一个mock对象
    • 10.2 在被置换的方法上设置期望,会不起作用
    • 10.3 Partial mock 不能在某些特定的类使用
    • 10.4 某些特定的类不能被置换和验证
    • 10.5 NSString的类方法不能被置换和验证
    • 10.6 NSObject 的方法不能被验证
    • 10.7 apple 的私有方法不能被验证
    • 10.8 Verify-after-running不能使用延时

OCMock 3 参考

1.创建Mock对象

1.1 类Mock

id classMock = OCMClassMock([SomeClass class]);

1.2 协议Mock

id protocolMock = OCMProtocolMock(@protocol(SomeProtocol));

1.3 严格的类和协议Mock

默认的mock方式是nice(方法调用的时候返回nil或者是返回正确的方法)

严格的模式下,mock的对象在调用没有被stub(置换)的方法的时候,会抛出异常.

id classMock = OCMStrictClassMock([SomeClass class]);id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol));

1.4 部分Mock

id partialMock = OCMPartialMock(anObject)

这样创建的对象在调用方法时:

  • 如果方法被stub,调用stub后的方法.
  • 如果方法没有被stub,调用原来的对象的方法.

partialMock 对象在调用方法后,可以用于稍后的验证此方法的调用情况(被调用,调用结果)

1.5 观察者Mock

id observerMock = OCMObserverMock();

这样创建的对象可以用于观察/通知.

2 置换方法

2.1 置换方法(待置换的方法返回objects)

OCMStub([mock someMethod]).andReturn(anObject);

在mock对象上调用某个方法的时候,这个方法一定返回一个anObject.(也就是说强制替换了某个方法的返回值为anObject)

2.2 置换方法(待置换的方法返回values)

OCMStub([mock aMethodReturningABoolean]).andReturn(YES);

在mock对象上调用某个方法的时候,这个方法一定返回values.

注意这里的原始值类型一定要和原来的方法的返回值一致.

2.3 委托到另一个方法(置换委托方法到另外一个方法)

OCMStub([mock someMethod]).andCall(anotherObject, @selector(aDifferentMethod));

置换mock 对象的someMethod ==> anotherObject 的aDifferentMethod.

这样,当mock对象调用someMethod方法的时候,实际上的操作就是anotherObject 调用了aDifferentMethod方法.

2.4 置换一个blcok方法.

OCMStub([mock someMethod]).andDo(^(NSInvocation *invocation) { /* block that handles the method invocation */ });

在mock对象调用someMethod的时候,andDo后面的block会调用.block可以从NSInvocation中得到一些参数,然后使用这个NSInvocation对象来构造返回值等等.

2.5 置换方法的参数

OCMStub([mock someMethodWithReferenceArgument:[OCMArg setTo:anObject]]);

OCMStub([mock someMethodWithReferenceArgument:[OCMArg setToValue:OCMOCK_VALUE((int){aValue})]]);

mock对象在调用某个带参数的方法的时候,这个方法的参数可以被置换.

setTo用来设置对象参数,setToValue用来设置原始值类型的参数.

2.6 调用某个方法就抛出异常

OCMStub([mock someMethod]).andThrow(anException);

当mock对象调用someMethod的时候,就会抛出异常

2.7 调用某个方法就发送通知

OCMStub([mock someMethod]).andPost(aNotification);

当mock对象调用someMethod的时候,就会发送通知.

2.8 链式调用

OCMStub([mock someMethod]).andPost(aNotification).andReturn(aValue);

所有的actions(比如andReturn,andPost)可以链式调用.上面的例子中,mock对象调用someMethod方法后,发送通知,返回aValue

2.9 转发的原来的对象/类

OCMStub([mock someMethod]).andForwardToRealObject();

使用部分mock的时候,使用类方法的可以转发到原来的对象/原来的类.

这个功能在链式调用或者是使用expectation的时候很有用.

2.10 什么也不做

OCMStub([mock someMethod]).andDo(nil);

可以给andDo传入nil参数,而不是原来一个block作为参数.

这个功能在使用部分mock/mock类的时候很有用,可以屏蔽原来的行为.

3 验证作用

3.1 运行后就验证

id mock = OCMClassMock([SomeClass class]);

/* run code under test */

OCMVerify([mock someMethod]);

在mock对象调用someMethod后就开始验证.(如果这个方法没有被调用),就抛出一个错误.

在验证语句中可以使用 参数约束.

3.2 置换后验证

id mock = OCMClassMock([SomeClass class]);
OCMStub([mock someMethod]).andReturn(myValue);

/* run code under test */

OCMVerify([mock someMethod]);

在置换某个方法(置换了返回的参数)后,然后可以验证这个方法是否被调用.

4 参数约束

4.1 任意参数约束

OCMStub([mock someMethodWithAnArgument:[OCMArg any]])
OCMStub([mock someMethodWithPointerArgument:[OCMArg anyPointer]])
OCMStub([mock someMethodWithSelectorArgument:[OCMArg anySelector]])

不管传递什么参数,对于所有活跃的invocations,置换该方法.Pointers 和selectors 需要像上面一样特殊对待.对于既不是对象,也不是指针,更不是SEL类型的,不可以忽略的参数,可以使用 any 来代替.

4.2 忽略非对象的参数

[[[mock stub] ignoringNonObjectArgs] someMethodWithIntArgument:0]

在这个invocation中,mock忽略所有的非对象参数.mock对象将会接收所有的someMethodWithIntArgument 方法 invocation,而不去管实际传递进来的参数是什么.如果这个方法含有对象参数和非对象参数,对象参数仍然可以使用OCMArg的参数约束.

4.3 匹配参数

OCMStub([mock someMethod:aValue)
OCMStub([mock someMethod:[OCMArg isNil]])
OCMStub([mock someMethod:[OCMArg isNotNil]])
OCMStub([mock someMethod:[OCMArg isNotEqual:aValue]])
OCMStub([mock someMethod:[OCMArg isKindOfClass:[SomeClass class]]])
OCMStub([mock someMethod:[OCMArg checkWithSelector:aSelector onObject:anObject]])
OCMStub([mock someMethod:[OCMArg checkWithBlock:^BOOL(id value) { /* return YES if value is ok */ }]])

如果在置换创建的时候,有个一个参数传递进来了,置换方法将仅仅匹配精确参数的invocations.带不同的参数来调用的方法不会被匹配.

OCMArg类提供了几个不同的方法来匹配不同的参数类型.

对于checkWithSelector:onObject:方法, 当mock对象接收到someMethod:的时候, 会触发 anObject上的aSelector方法. 如果方法带参数,这个参数会传递给someMethod:. 这个方法应该返回一个BOOL值,表示这个参数是否和预期的一样.

4.4 使用Hamcrest来匹配

OCMStub([mock someMethod:startsWith(@"foo")]

OCMock不带 Hamcrest 框架,所以如果想要使用的话,需要自己安装Hamcrest .

5 类方法的Mock

5.1 置换类方法

id classMock = OCMClassMock([SomeClass class]);
OCMStub([classMock aClassMethod]).andReturn(@"Test string");

// result is @"Test string"
NSString *result = [SomeClass aClassMethod];

置换类方法和置换实例方法的步骤相像.但是mock对象在深层次上对原有 类做了些更改.(替换了原有的的类的meta class).这让置换调用直接作用在mock对象上,而不是原有的类.

注意:

添加到类方法上的mock对象跨越了多个测试,mock的类对象在置换后不会deallocated,需要手动来取消这个mock关系.

如果mock对象作用于同一个类, 这时的行为就不预测了.

5.2 验证类方法的调用

id classMock = OCMClassMock([SomeClass class]);

/* run code under test */

OCMVerify([classMock aClassMethod]);

验证类方法的调用和验证实例方法的调用的使用方式一样.

5.3 有歧义的类方法和实例方法

id classMock = OCMClassMock([SomeClass class]);
OCMStub(ClassMethod([classMock ambiguousMethod])).andReturn(@"Test string");

// result is @"Test string"
NSString *result = [SomeClass ambiguousMethod];

置换了类方法,但是类有一个和类方法同名的实例方法,置换类方法的时候,必须使用ClassMethod()

5.4 恢复类

id classMock = OCMClassMock([SomeClass class]);

/* do stuff */

[classMock stopMocking];

置换类方法后,可以将类恢复到原来的状态,通过调用stopMocking来完成.

如果在结束测试前,需要恢复到原来的状态的话,这就很有用了.

在mock对象被释放的时候,stopMocking会自动调用.

当类恢复到原来的对象,类对象的meta class会变为原来的meta class.这会移除所有的方法置换.

在调用了stopMocking之后,不应该继续使用mock对象.

6 部分Mock

6.1 置换方法

id partialMock = OCMPartialMock(anObject);
OCMStub([partialMock someMethod]).andReturn(@"Test string");

// result1 is @"Test string"
NSString *result1 = [partialMock someMethod];

// result2 is @"Test string", too!
NSString *result2 = [anObject someMethod];

部分Mock修改了原有的mock对象的类.(实际上是继承了待mock对象,然后替换用 继承的类来代替原有的类).

这就是说: 使用真实的对象来调用,即使是使用self,也会影响 置换方法和预期的结果.

6.2 验证方法调用

id partialMock = OCMPartialMock(anObject);

/* run code under test */

OCMVerify([partialMock someMethod]);

验证方法的调用和验证类方法,验证协议的调用类似.

6.3 恢复对象

id partialMock = OCMPartialMock(anObject);

/* do stuff */

[partialMock stopMocking];

真正的对象可以通过调用stopMocking方法来恢复到原来的状态.

这种情况只有在结束测试之前需要恢复到原来状态.

部分mock对象会在释放的时候,会自动调用 stopMocking方法.

当对象转变为原来的状态后,类会变为原来的类.也会移除所有的置换方法.

在调用了stopMocking之后,最好不要去使用mock对象.

7 严格mock和期望

7.1 Expect-run-verify 期望-运行-验证

id classMock = OCMClassMock([SomeClass class]);
OCMExpect([classMock someMethodWithArgument:[OCMArg isNotNil]]);

/* run code under test, which is assumed to call someMethod */

OCMVerifyAll(classMock)

这是使用mock最原始的方法:

  1. 创建mock对象
  2. 期望调用某个方法
  3. 测试代码(预想的是这段测试代码会调用上面期望调用的方法.
  4. 验证mock对象(也就是验证期望的方法是否被调用了)

如果预期的方法没有被调用,或者调用的时候,传递的参数不对,那么就好产生错误.可以使用上面 参数约束.

严格的mock可以用在类和协议上.

如果有怀疑的话,可以使用 3 验证作用

7.2 严格的mock 和快速失败

id classMock = OCMStrictClassMock([SomeClass class]);
[classMock someMethod]; // this will throw an exception

上面mock没有设置任何期望,直接掉调用某个方法会抛出异常.

当超出去预期的调用的时候,会立即测试失败. 只有strict mock才会快速失败.

7.3 置换操作和预期

id classMock = OCMStrictClassMock([SomeClass class]);
OCMExpect([classMock someMethod]).andReturn(@"a string for testing");

/* run code under test, which is assumed to call someMethod */

OCMVerifyAll(classMock)

可以使用andReturn,andThrow,等预期的操作.如果方法被调用,会调用置换 方法,确认方法确实被调用了.

7.4 延时验证

id mock = OCMStrictClassMock([SomeClass class]);
OCMExpect([mock someMethod]);

/* run code under test, which is assumed to call someMethod eventually */

OCMVerifyAllWithDelay(mock, aDelay);

在某种情况下,预期的方法只有在 run loop 出于活跃状态的时候才会被调用.这时,可以将认证延时一会.aDelay是mock对象会等待的最大时间.通常情况下,在预期达到后就会返回.

7.5 依序验证

id mock = OCMStrictClassMock([SomeClass class]);
[mock setExpectationOrderMatters:YES];
OCMExpect([mock someMethod]);
OCMExpect([mock anotherMethod]);

// calling anotherMethod before someMethod will cause an exception to be thrown
[mock anotherMethod];

mock会按照在预期中设置好的顺序来判断.只要调用的不是按照期望的调用顺序,这个时候就会抛出异常.

8 观察者mock

8.1 准备工作

id observerMock = OCMObserverMock();
[notificatonCenter addMockObserver:aMock name:SomeNotification object:nil];
[[mock expect] notificationWithName:SomeNotification object:[OCMArg any]];
  1. 为观察者和通知创建一个mock对象.
  2. 在通知中心注册对象
  3. 预期会调用这个通知.

8.2 验证

OCMVerifyAll(observerMock);

目前观察者 mock 总是严格的mock.当一个不在预期中的通知调用的时候,就会抛出一个异常.

这就是说,单个的通知实际上不是能被验证的.所有的通知必须按照预期赖设置.他们会在通过调用OCMVerifyAll来一起验证.

9 进阶话题

9.1 对于普通的mock,快速失败

对strict mock 对象,在一个mock对象上调用没有被mock方法(没有被置换)的时候,会抛出一个异常,这时候会发生 快速失败.

id mock = OCMClassMock([SomeClass class]);
[[mock reject] someMethod];

这种情况下,mock会接受除了someMethod 的所有方法.触发someMethod方法会导致快速失败.

9.2 在OCMVerifyAll时重新抛出异常

在fail-fast的时候会抛出异常,但是这并不一定会导致测试失败.

通过调用OCMVerifyAll重新抛出异常可以导致测试失败.

这个功能在不在预期中的从notifications引发的invocations出现的时候使用.

9.3 置换创建对象的方法

id classMock = OCMClassMock([SomeClass class]);
OCMStub([classMock copy])).andReturn(myObject);

可以置换创建对象的 类/实例方法.当被置换的方法以 alloc,new,copy,mutableCopy开头的方法时,OCMock会自动调整对象的引用计数.

id classMock = OCMClassMock([SomeClass class]);
OCMStub([classMock new])).andReturn(myObject);

尽管可以置换类的new方法,但是不建议这么做.

没有办法置换 init 方法,因为这个方法是被mock对象自己实现的.

9.4 基于实例对象的方法替换

id partialMock = OCMPartialMock(anObject);
OCMStub([partialMock someMethod]).andCall(differentObject, @selector(differentMethod));

用一句话概括起来,Method swizzling 会在运行时替换一个方法的实现.

使用 partial mock然后调用 andCall操作可以实现这个方法替换.

当anObject收到someMethod消息时,anObject的实现没有触发,相反的,

differentObject的differentMethod得到调用.

其他方法并不会收到影响,仍然会调用原来的的方法的实现.

10 使用限制

10.1 在一个指定的类上,只能有一个mock对象

// don‘t do this
id mock1 = OCMClassMock([SomeClass class]);
OCMStub([mock1 aClassMethod]);
id mock2 = OCMClassMock([SomeClass class]);
OCMStub([mock2 anotherClassMethod]);

原因是类的meta class 替换后,不会释放,mock类仍会存在,甚至可能跨tests.

如果多个相同mock对象管理同一个类,运行时的行为就不可确定.

10.2 在被置换的方法上设置期望,会不起作用

id mock = OCMStrictClassMock([SomeClass class]);
OCMStub([mock someMethod]).andReturn(@"a string");
OCMExpect([mock someMethod]);

/* run code under test */

OCMVerifyAll(mock); // will complain that someMethod has not been called

上面代码先替换了someMethod,然后强制someMethod返回”a string”

由于现在mock的实现,所有的someMethod都会置换所处理.所以,即使这个方法被调用,这个验证也会失败.

可以通过在expect后添加andReturn来避免这个问题. 也可以通过在expect后再次设置一个方法替换.

10.3 Partial mock 不能在某些特定的类使用

id partialMockForString = OCMPartialMock(@"Foo");
// will throw an exception

NSDate *date = [NSDate dateWithTimeIntervalSince1970:0];
id partialMockForDate = OCMPartialMock(date);
// will throw on some architectures

不可能创建一个 toll-free bridged的类,例如 NSString,或者是NSDate.

如果你试图这么去做,那么可能会抛出一个异常.

10.4 某些特定的类不能被置换和验证

id partialMockForString = OCMPartialMock(anObject);
OCMStub([partialMock class]).andReturn(someOtherClass); // will not work

不能mock某些运行时的方法,例如

  • class,
  • methodSignatureForSelector:
  • forwardInvocation:

10.5 NSString的类方法不能被置换和验证

id stringMock = OCMClassMock([NSString class]);
// the following will not work
OCMStub([stringMock stringWithContentsOfFile:[OCMArg any] encoding:NSUTF8StringEncoding error:[OCMArg setTo:nil]]);

10.6 NSObject 的方法不能被验证

id mock = OCMClassMock([NSObject class]);

/* run code under test, which calls awakeAfterUsingCoder: */

OCMVerify([mock awakeAfterUsingCoder:[OCMArg any]]);
// still fails

不可能在NSObject 和它的分类category上使用verify-after-running.

在某些情况下可能置换这个方法,然后验证.

10.7 apple 的私有方法不能被验证

UIWindow *window = /* get window somehow */
id mock = OCMPartialMock(window);

/* run code under test, which causes _sendTouchesForEvent: to be invoked */

OCMVerify([mock _sendTouchesForEvent:[OCMArg any]]);
// still fails

含有下划线前缀,后缀,NS,UI开头的方法等.

10.8 Verify-after-running不能使用延时

只有在 严格的mock和期望中,可以使用expect-run-verify

时间: 2024-10-04 21:28:20

OCMock 3 参考的相关文章

Podfile语法参考(译)

https://www.jianshu.com/p/8af475c4f717 2015.10.30 19:14* 字数 2496 阅读 35976评论 9喜欢 120 本文翻译自官方的Podfile Syntax Reference,也参考了之前一位博主的翻译,若翻译中有什么异议,欢迎留言指正. Podfile Podfile文件详细描述了一个或多个工程中targets的依赖关系.Podfile会默认创建一个隐式的目标链接到工程中用户的第一个target,名称为"default". 一

【资源共享】《Camera_for_RockChipSDK参考说明_v4.1》下载

关于摄像头调试的文档<Camera_for_RockChipSDK参考说明_v4.1> 下载地址:http://developer.t-firefly.com/thread-12429-1-1.html

angular参考手册拷贝

AngularJS 参考手册 AngularJS 指令 本教程用到的 AngularJS 指令 : 指令 描述 ng-app 定义应用程序的根元素. ng-bind 绑定 HTML 元素到应用程序数据 ng-bind-html 绑定 HTML 元素的 innerHTML 到应用程序数据,并移除 HTML 字符串中危险字符 ng-bind-template 规定要使用模板替换的文本内容 ng-blur 规定 blur 事件的行为 ng-change 规定在内容改变时要执行的表达式 ng-check

分析实现zc706 AMP-linux和裸奔的注意点更新-参考xapp1078 latest info.

在上一篇博文中,研究了xapp1078,它的目标平台是zc702.本文分析在zc706实现AMP时需要修改的关键点.参考资料:http://www.wiki.xilinx.com/XAPP1078+Latest+Information 1,zc706中的bootROM中,采用了V2版本.该版本使用了不同的WFE loop,CPU1被唤醒(中断.sev或者其他事件)后,会读取0xFFFFFFF0位置的数据与0xFFFFFF2C比较,如果与0xFFFFFF2C相同,则回到WFE状态(待机).如果与0

.net 自己写的操作Excel 导入导出 类(以供大家参考和自己查阅)

由于现在网页很多都关系到Excel 的操作问题,其中数据的导入导出更是频繁,作为一个菜鸟,收集网上零散的知识,自己整合,写了一个Excel导入到GridView ,以及将GridView的数据导出到EXCEL的类方法,以供参考和方便自己以后查阅. 1 #region 引用部分 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Dat

排序算法--概述和参考

1 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 2 参考 http://blog.csdn.net/hguisu/article/details/77

Hyper-v 安装CentOS 7 (其他虚拟机一样参考)

平台之大势何人能挡? 带着你的Net飞奔吧!http://www.cnblogs.com/dunitian/p/4822808.html hyper-v安装很多人没弄过,我这里介绍一下.(其他虚拟机参考==> 点我吧) 右击,新建虚拟机 #通用部分开始 收工~~~ Linux 开机时网络自动连接:http://www.cnblogs.com/dunitian/p/4975830.html ifconfig: command not found(CentOS专版,其他的可以参考)http://ww

MVC 构建图片/文件选择器 参考其它CMS功能

实现结果,如下 点击选择图片,弹出一个iframe框 顶部默认图片根目录,依次下面是文件列表 底部是选择的文件地址,以及上传新的图片和文件 加载iframe 调用js方法 function initFiles(path) { if (path == spath) { $("#fback").addClass("hidden"); } else { $("#fback").removeClass("hidden"); } var

Docker run 参考指南

Docker run参考指南 docker运行在一个独立的隔离的进程中. 当用户执行dockerrun,它将启动一个有着独立的文件系统,独立的网络和独立的进程树的进程. 基本的docker run命令的格式: docker run  [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] [OPTIONS]分为两种: 1.对于用户独占性的设置: 前台.后台运行 容器定义 网络设定 容器在CPU和内存中的运行时间 运行权限和LXC配置 2.在操作者和开发者之间的共享设定,