ocmock
Unit testing is one of the most important parts of software development. It is basically testing small parts of the codes individually by using public interfaces of objects. While developing an object, other objects, modules or libraries are used as dependencies and they are injected to the code to be utilised.
单元测试是软件开发中最重要的部分之一。 它基本上是通过使用对象的公共接口来单独测试代码的一小部分。 开发对象时,其他对象,模块或库用作依赖项,并将它们注入要使用的代码中。
In unit testing, only the developed code which is called as System Under Test (SUT) should be tested and mock version of other dependencies should be injected as a black box. Creating a mock version of an object can be achieved by two ways
在单元测试中,仅应测试开发的代码(称为“被测系统”(SUT)),并应将其他依赖项的模拟版本作为黑盒注入。 创建对象的模拟版本可以通过两种方式实现
Manual mocking is creating a dummy object by inheriting from the original object and overriding public interfaces or creating a dummy implementing of a protocol. This is the widely used method in Swift unit tests. However, the number of dummy implementations may grow too much.
手动模拟是通过从原始对象继承并覆盖公共接口或创建协议的虚拟实现来创建虚拟对象。 这是Swift单元测试中广泛使用的方法。 但是,虚拟实现的数量可能会增加太多。
In memory mocking is creating a mock object of a class or protocol and configuring it on place. This is the widely used method in Objective-C (ObjC) unit tests. The objects can be disposed when test is done, thus there is no need for dummy implementations.
在内存中,模拟是创建类或协议的模拟对象并就地配置它。 这是在Objective-C(ObjC)单元测试中广泛使用的方法。 测试完成后可以放置对象,因此不需要虚拟实现。
OCMock is a great library for mocking in ObjC. It has a lot of features which can fulfil almost every need while testing codes. It can create mock objects of classes or protocols, predefine the return values of the public interfaces or capture the arguments sent to methods etc.
OCMock是一个很好的ObjC模拟库。 它具有许多功能,可以满足测试代码时几乎所有的需求。 它可以创建类或协议的模拟对象,预定义公共接口的返回值或捕获发送到方法等的参数。
OCMock uses Swizzling technique to create mock objects and Swizzling can only be used in ObjC which this is one of the reasons why we cannot to use it with pure Swift.
OCMock使用Swizzling技术创建模拟对象,而Swizzling只能在ObjC中使用,这是我们不能与纯Swift一起使用的原因之一。
OCMock returns id
from its methods because, in ObjC world, the most powerful generic is id
where we can send any call invocation on to it. This means ObjC is not a very type-safe language and this is turned into a benefit while mocking. However, Swift is strict on type safety and this is another reason why we cannot use it with Swift.
OCMock从其方法返回id
,因为在ObjC世界中,最强大的泛型是id
,我们可以在其上发送任何调用调用。 这意味着ObjC不是一种非常类型安全的语言,并且在进行模拟时会变成一种好处。 但是,Swift对类型安全性要求严格,这是我们不能在Swift中使用它的另一个原因。
ObjC supports lightweight generics which is an adaptation of header files for different types and using the most powerful generic id
underneath. This can be utilised to overcome the type-safety problem. By that way, ObjC objects, protocols and @objc
tagged Swift protocols can be mocked and used in Swift unit tests.
ObjC支持轻量级泛型,这是针对不同类型的头文件的改编,并在其下使用了最强大的泛型id
。 这可以用来克服类型安全问题。 这样,可以模拟ObjC对象,协议和@objc
标记的Swift协议,并将其用于Swift单元测试中。
![Image for post](https://miro.medium.com/max/9999/1*FuI_0xmtlwwm-ZBsfiDrpg.png)
A sample header definition can be like the one shown on the left. This object will create a mock object
of the given type and pre-define the object’s behaviours.
示例标头定义可以类似于左侧所示。 该对象将创建给定类型的模拟object
并预定义该对象的行为。
![Image for post](https://miro.medium.com/max/9999/1*4-shbstemc_JgLRzVgC65w.png)
Implementation of the class will use OCMock methods to create mock objects or stub methods as shown on the left. As seen the return types are id
since ObjC implementation files cannot use generic definitions. However, since the return type of the methods is defined with generic types in the header file, this manipulation will trick Swift to use that as desired types.
该类的实现将使用OCMock方法创建模拟对象或存根方法,如左侧所示。 如图所示,返回类型是id
因为ObjC实现文件不能使用泛型定义。 但是,由于方法的返回类型是在头文件中使用通用类型定义的,因此此操作将使Swift欺骗以将其用作所需类型。
Now let’s assume a UIViewContoller
is injected into an object and the object calls view
property in the implementation like below.
现在,假设将UIViewContoller
注入到对象中,并且该对象在如下所示的实现中调用view
属性。
![Image for post](https://miro.medium.com/max/9999/1*ycL9vmF4mJzrgc00q4tCdw.png)
If a mock object is injected into this method without any expectation, the test would fail with invalid invocation with unexpected method error as follows.
如果在没有任何预期的情况下将模拟对象注入到此方法中,则测试将失败,并且调用无效,并出现意外的方法错误,如下所示。
![Image for post](https://miro.medium.com/max/9999/1*xucNhKGY8fuLIXMXk1uyrw.png)
Here the method call should be expected to make the test valid as follows
这里应该期望方法调用使测试有效,如下所示
![Image for post](https://miro.medium.com/max/9999/1*H3fBszULEBDfzJ9ara45pA.png)
The mock view controller returns nil
value for its view
property and the test passes. Now, let’s test nonnull
case of this method. To do that, our mock object should return a view when view
property is called and this can be done by using the andReturn
method of our wrapper.
模拟视图控制器为其view
属性返回nil
值,并且测试通过。 现在,让我们测试此方法的nonnull
情况。 为此,我们的模拟对象应该在调用view
属性时返回一个视图,这可以通过使用包装器的andReturn
方法来完成。
![Image for post](https://miro.medium.com/max/9999/1*azRdO2odnFqqIa7Xb0JFEA.png)
As seen on the above test, the mock object returns an empty view for the property and test passes with this mock value.
如上述测试所示,模拟对象返回该属性的空视图,并使用此模拟值进行测试。
Injecting a dependency can be done in many ways and the most common ones are direct injection of objects and injection of protocols. The latter brings a lot of benefits into codes and helps developers to follow SOLID principles. A mock implementation of an ObjC protocol can be achieved by OCMock with a similar way to class mocking and unfortunately, Swift protocols cannot be mocked as well. However, the good news is that while there is no way to mock a Swift class, @objc
tagged Swift protocols can be mocked in the same way. So, choosing dependency injection by protocols makes development code more testable in this way.
注入依赖关系可以通过多种方式完成,最常见的依赖关系是对象的直接注入和协议的注入。 后者为代码带来了很多好处,并帮助开发人员遵循SOLID原则。 ObjC协议的模拟实现可以由OCMock以类似于类模拟的方式来实现,不幸的是,Swift协议也不能被模拟。 但是,好消息是,虽然没有办法模拟Swift类, @objc
可以用相同的方式模拟@objc
标记的Swift协议。 因此,通过协议选择依赖注入可以使开发代码更易于测试。
This story contains very basic use cases and this can be extended too much to cover most of the OCMock features such as static method mocking, method expectation with any arguments, gathering arguments from invocations etc. I hope it helps.
这个故事包含非常基本的用例,并且可以扩展到涵盖大多数OCMock功能,例如静态方法模拟,带有任何参数的方法期望,从调用中收集参数等。我希望对您有所帮助。
Thank you for reading and happy testing.
感谢您的阅读和愉快的测试。
翻译自: https://medium.com/macoclock/using-ocmock-with-swift-4f5f87930ae7
ocmock