iOS Unit Testing With OCMock
单元测试是我们保障代码质量的重要手段, Apple 对此也十分重视,这点可以从 Xcode 新建工程时会自动创建单元测试的 Target 看出来。单元测试牵涉的内容很多,这篇文章是目前我对单元测试的理解。
既然是单元测试,那么什么是单元呢?我没有去考证,但我是这么理解的:面向对象编程范式里一切皆对象,而对象是由实例变量和方法组成,对象之间通过方法互相作用,我们的应用可以看作是一个对象图,对象图上的对象相互作用来实现我们的需求。这么看来我们的单元应该是对象的方法。
对象的方法在工作的时候可能要依赖其他对象,为了去除依赖对象对测试的影响,我们引入 Mock.
Mocks are ‘fake’ objects with pre-defined behavior to stand-in for concrete objects during testing. – HackaZach
引用 Mock 之后,我们在单元测试中如何使用它呢?
The general recipe for using mocks in unit-tests is:
- Create the mock object
- Specify the expected invocations and return values
- Associate the mock object with the code under test
- Execute the code under test
- Validate that your assertions are correct
Create the mock object
OCMock 中创建 Mock 对象的方法如下:
Factory Method | Description |
---|---|
+mockForClass: | Create a mock based on the given class |
+mockForProtocol: | Create a mock based on the given protocol |
+niceMockForClass: | Create a “nice” mock based on the given class |
+niceMockForProtocol: | Create a “nice” mock based on the given protocol |
+partialMockForObject: | Create a mock based on the given object |
+observerMock: | Create a notification observer (more on this later) |
Mock 对象是只是一个空壳,只能调用预先定义的方法,调用没有预先定义的方法会抛出异常;
Nice Mock 对象也是一个空壳,只是它会忽略没有预先定义的方法不会抛出异常;
Partial Mock 对象是把一个已有的对象转成 Mock,调用没有预先定义的方法,它会把方法传给已存在的对象;
Observer Mock 对象是用来观察通知的。
Specify the expected invocations and return values
Calling either the -expect or -stub method will return an object that you can use to setup your expectations.
Specify the expected invocations
- 不带参数的方法
1 2 3 4 5 6 7 8 9 |
|
2. 带参数的方法
You can use any of the following OCMArg class methods in place of a real argument when setting up your method expectations:
OCMArg method | Description |
---|---|
+any | Any argument is accepted. |
+anyPointer | Accepts any pointer |
+isNil | The given argument must be nil |
+isNotNil | The given argument must not be nil |
+isNotEqual: | Given argument is not object-equivalent with expectation |
+checkWithSelector: | onObject: Check the argument with the given action/target pair |
+checkWithBlock: | Check the argument with the given block (OS X 10.6 or iOS 4) |
OCMock also provides a few handy macros for argument matching:
Macro | Description |
---|---|
OCMOCK_ANY() | Equivalent to [OCMArg any] |
OCMOCK_VALUE(value) | A quick way to match a non-object argument |
CONSTRAINT(selector) | Validate with a given selector on self |
CONSTRAINTV(selector,value) | Validate with a given selector on self and an additional argument |
1 2 3 4 5 6 |
|
3. 通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
- Specify the return values
If methods on your mocks need to return values, you have a variety of methods to call on the object returned by -expect or -stub.
Method | Explanation |
---|---|
-andReturn: | Return the given object |
-andReturnValue: | Return a non-object value (wrapped in a NSValue) |
-andThrow: | Throw the given exception |
-andPost: | Post the given notification |
-andCall:onObject: | Call the selector on the given object |
-andDo: | Invoke the given block (only on OS X 10.6 or iOS 4) |
Execution & Validation
- Associate the mock object with the code under test
- Execute the code under test
- Validate that your assertions are correct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Q:How to mocking singleton? A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Reference:Objective-C Singleton Pattern Updated For Testability
Reference:
OCMock
Making Fun of Things with OCMock
OCMock Test Origami
IMPROVING IOS UNIT TESTS WITH OCMOCK