Objective-C中的单元测试

飞行猕猴桃

每种语言都有自己的规则。 查看语法,语言功能或语言类型都没关系。 一切都改变了,同时保持不变。 在我们的主题应用程序中,我们将使用swift,但是由于Objective-C是iOS开发的起源,因此我们绕了弯路,以了解此处的具体操作。

我不是XCTest的忠实拥护者。 不要误会我的意思,它确实可以正常工作..但是就这样..就可以了。 了解我们要测试的应用程序和操作的整体状态,取决于我们命名测试的能力。 总的来说,我们很讨厌这样做。 一种替代方法是基于RSpec的框架。 他们在我们的测试中添加了语法糖,可能只是为了了解测试2小时或解决问题而有所不同。

猕猴桃

对于Objective-C,这是猕猴桃。 它提供了我们可以描述,正在测试什么以及处于何种状态的不同阶段:

  • describe :描述被测系统(sut)
  • 上下文 :描述缝合状态

关于这些定义,我们可以编写一个带有演示类的简单测试文件结构:

私下入侵

我已经提到每种语言都有自己的怪癖。 在Objective-C中,私有不存在。 相反,它仅对编译器可见。 我们仍然可以通过运行时将消息发送到对象的特定实例,从而忽略可见性。

好的..我们不会开始使用objc_msgSend。 不用担心 相反,我们可以使用类别来完成这项工作。 在我们的测试类中,我们将添加一个类别,其中包含我们要测试的所有私有属性和方法,这些属性和方法无法从外部访问。 这样做将为编译器提供可见性,即使它们被认为是私有的,我们也可以将任何消息发送给对象。

存根

时不时地,我们不得不依靠我们无法控制的API。 这可以是简单的SDK API或网络服务。
想象一下有一个登录视图控制器。 您如何测试呢? 尤其是当您的测试应该在没有外部依赖性的黑暗洞穴中运行时,该怎么办?
欢迎进行嘲笑和存根。 在本文中,我们将讨论如何在单元测试和集成测试中对方法调用进行存根。 存根的想法是通过指定返回值或简单地替换它来定义方法调用的行为。要存根方法,我们需要对象对此方法做出响应。 有了这个,我们可以简单地通过编写以下代码来定义返回值:

  [testClass存根:@selector(方法)和Return:Value]; 

该值可以是任何值:布尔值,NSString,NSInteger或您返回的任何值。 猕猴桃不在乎。 这样,我们可以简单地替换本地网络服务器的登录方法。
是不是很简单? 但是,当我们无法获得所用类的实例时,我们该怎么办?

铁杆存根

为此,有方法令人毛骨悚然。 这是Objective-C内的黑魔法的一部分,不应粗心使用。 但是,如果您知道自己在做什么,并且将其用于测试,则可能会从中获得一些价值。

那么什么是方法混乱? 基本上在运行时,您将另一个方法添加到一个类中,并用另一个方法切换该方法的签名。 关于我们先前描述的登录方法,让我们添加另一个名为swizzleLogin的方法。 通过使用swizzleLogin修改登录方法,现在每次登录都将执行swizzleLogin,反之亦然。
这很容易做错,所以让我们介绍一下JRSwizzle。 这将正确地混淆您的方法。

那么我该如何使用呢?

1.创建一个类别
2.覆盖负载
3.创建Swizzle方法

因此,让我们看一下结果如何。 我有一个名为DemoModel的类,我想介绍其文本方法。 因为我想确保它只执行一次(想象一下就打了两次..我们得到了原始结果),所以将它们全部包装在dispatch_once调用中。

而已。 现在,无论何时调用DemoStore中的文本方法,都将其重定向到swizzleName。 如果您仍然想在执行swizzleName之后仍然调用原始文本方法,只需在原始swizzleName方法中调用swizzleName。
是的,应该在您的头上响起警钟,但是由于我们已重定向,因此在这种情况下,只需调用原始文本方法即可。

另一个有趣的部分是,将这个类别添加到目标中就足够了。 您不必在任何地方包含它。 因此请小心,并确保它不包含在您的主要目标中。

结论

如您所见,在Objective-C中进行测试时,有很多细节要知道。 了解这些将有助于您改善测试。 这篇文章绝不会穷尽所有测试,存根和嘲弄的可能性。 以后我们可能还会看其他好的框架(例如OCMock)

Next:Swift中的单元测试

上一篇:测试驱动开发