资讯专栏INFORMATION COLUMN

iOS测试规范

spademan / 3062人阅读

摘要:而且,构造函数不应该包含行为,所以没有值得测试的东西。一旦一个被创建了,你测试它的不为空因为你知道构造函数创建了一个新的并将它赋给了变量。现在为类的构造函数编写的测试出问题了,即便类运行良好,但并没有包括。

在研究了有关测试的相关内容,发现有些被忽略掉的很明显的问题。

1、为什么需要测试

从积极的方面来说,写自动化测试的优势如下:

使重构更简单 —— 你可以自信的修改实现细节,而不用去触及公有 API。

避免代码恶化—— 恶化在什么时候发生?在你修改代码的时候。

提供了可执行的说明和文档 —— * 你在什么时候更想知道软件实际上是如何工作的?在你想修改它们的时候

减少了创建软件的时间 —— 怎么减少时间的?是通过更快速地修改你的代码,出错时测试会自信地告诉你哪里出错了

降低了创建软件的代价 —— 好吧,时间就是金钱,我的朋友

其实上述优势最大的就是,当我们需要优化代码,进行修改时,会给予我们一种保障。所以在自动化测试的支撑下,我们需要做的就比较简单了。

2、测试的最佳实践指导

有 5 条被认为是每个测试都应该遵守的基本原则。便于记忆这 5 条规则的缩写是: F.I.R.S.T.

测试应该:

很快速(Fast) —— 测试应该能够被经常执行。

能隔离(Isolated) —— 测试本身不能依赖于外部因素或者其他测试的结果。

可重复(Repeatable) —— 每次运行测试都应该产生相同的结果。

带自检(Self-verifying) —— 测试应该包括断言,不需要人为干预。

够及时(Timely) —— 测试应该和生产代码一同书写。

坏的实践

不要测试私有方法

私有方法意味着私有。如果你感到有必要测试一个私有方法,那么那个私有方法一定含有概念性错误,通常是作为私有方法,它做的太多了, 从而违背了单一职责原则

如果仅仅为了测试,就让私有方法变为公有,那么就会改变方法的私有性,会有被其他类调用的风险。那么应该该如何去做呢。

通过这个类的公有 API。永远通过公有 API 测试你的代码。程序的公有 API 定义了一个约定,它是一组关于你的程序对应于不同输入时定义良好的一组期望。私有 API (私有方法或者整个类) 并没有定义约定,并且可以不经通知自行修改,所以你的测试 (或者你的同事) 不能依赖于它们。

通过这种方法测试你的私有方法,你可以自由地修改你的 (真正的) 私有代码,并且通过划分成只做一件事情,并经过正确测试的小的类,来提升代码的设计。

不要Stub 私有方法

Stub 私有方法和测试私有方法具有相同的危害,更重要的是,stub 私有方法将会使程序难以调试。

同样,当我们 stub 一个方法的时候,我们必须依据它做出的约定来进行。但是私有方法没有指定的约定的 —— 毕竟,这也是为什么它们是私有的原因。由于私有方法的行为可以不经通知自行修改,你的 stub 可能与实际情况背道而驰,但是你的测试仍然会通过。这是多么的可怕啊,让我们来看一个例子:

今天:一个类的公有方法依赖于该类的一个私有方法。这个私有方法 foo 永远不会返回空。为公有方法编写的测试为了方便起见,我们 stub 出了私有方法。当 stub foo 方法的时候,你永远不会考虑到 foo 返回为空的情况,因为现在这种情况永远不会发生。

明天:这个私有方法被修改了,现在它返回空了。它是一个私有方法,所以这没什么问题。为公有方法编写的测试不会相应地被修改 (“我正在修改一个私有方法,所以我为什么要更新我的测试?”)。公有方法现在在私有方法返回空的情况下会出错,但是测试仍然会通过!

不要 Stub 外部库

第三方代码不应该在你的测试中直接出现。

今天:你的网络部分的代码依赖于著名的 HTTP 库 LSNetworking.为了避免使用实际的网络 (为了让你的测试更快速更可信),你 stub 了那个库中的方法 -[LSNetworking makeGETrequest:],没有通过实际的网络合适地替代了它的行为 (它通过一个封装好的响应调用了执行成功的回调)。

明天:你需要使用一个替代品来取代 LSNetworking (可能是 LSNetworking 已经不再维护或者是你需要换成一个更先进的库,因为它有很多你需要的新特性等等)。这是一次重构,所以你不应该修改测试。你替换了库。你的测试会失败,因为依赖的网络没有被 stub (-[LSNetworking makeGETrequest:]不会被调用)。

应该做什么:测试中,依靠 stubbing 伞 (umbrella stubbing) 来替代那个库的全部功能。

stubbing 伞 (一个我刚刚发明的术语) 包括了对于所有你的代码可能用到的方式 -- 不管事现在还是将来 -- 的 stub。它们可以通过良好声明的 API 完成一些任务,而不去关心实现的细节。

不要测试构造函数

构造函数定义的是实现细节,你不应该测试构造函数,这是因为我们认同测试应该与实现细节解耦这一观点。而且,构造函数不应该包含行为,所以没有值得测试的东西。

今天:你有一个 Car 类,并包含一个构造函数。一旦一个 Car 被创建了,你测试它的 Engine 不为空 (因为你知道构造函数创建了一个新的 Engine 并将它赋给了变量 _engine)。

明天:Engine 类创建起来变得代价很高,所以你决定使用延迟初始化 (lazily initialize),在第一次调用 Engine 的 getter 方法时才初始化 Engine (这是很好的)。 现在为 Car 类的构造函数编写的测试出问题了,即便 Car 类运行良好,但 Car 并没有包括 Engine。另一个可能是你的测试不会失败,因为测试包含 Engine 的 Car 类会触发 Engine 的延迟加载。所以我的问题是:为什么还要测试?

3、如何进行测试

Given / When / Then模式
我们可以根据 Given-When-Then 模式来组织我们的测试用例,将测试用例拆分成三个部分。

在 given 部分里,通过创建模型对象或将被测试的系统设置到指定的状态,来设定测试环境。when 这部分包含了我们要测试的代码。在大部分情况,这里只有一个方法调用。在 then 这部分中 ,我们需要检查我们行为的结果:是否得到了我们期望的结果?对象是否有改变?这部分主要包括一些断言。简单一点来讲就是,设定环境、调用方法、检测结果。

简单的例子看起来是这个样子的:

- (void)testThatItDoesURLEncoding
{
    // given
    NSString *searchQuery = @"$content$amp;?@";
    HTTPRequest *request = [HTTPRequest requestWithURL:@"/search?q=%@", searchQuery];

    // when
    NSString *encodedURL = request.URL;

    // then
    XCTAssertEqualObjects(encodedURL, @"/search?q=%24%26%3F%40");
}

这种简单的模式使我们能够更容易地书写和理解这些测试用例,因为它们都遵循了同样的模式。为了更快地浏览,我们甚至会在每个部分的代码上写上 “given”,“when”,“then” 的注释。通过这种方式,这个方法就能很快被理解。

Mock

在iOS测试中的mock测试框架可以采用OCMock

我们用 mock 来管理一个对象的所有依赖项。通过这个方式,我们可以测试这个类在隔离情况下的行为。但是这里有个明显的缺点,那就是当我们修改了一个类后,其他依赖于这个类的类的单元测试不能自动失败。但是关于这一点我们可以通过集成测试来补救,因为它可以测试所有的类。

我们不应该‘过度mock’,也就是说,去 mock 除了被测试的对象的其他对象这样的习惯是要尽量避免的。当我们刚开始的时候,我们经常会这样做,我们甚至会 mock 那些简单到可以作为方法参数的对象。现在我们使用了不少真实的对象,而不是 mock 它们。

4、编写测试是一种投资

我们需要花时间编写和维护它们。我们可以证明这种投资有回报的唯一方法就是我们期望节省时间。将实现细节和测试耦合在一起会减少测试带来的回报,使得那些投资变得不合算,甚至在某些情况下变得一文不值。

在编写测试、重构以及修改系统行为的时候,检查你的测试在面对错误的原因时是失败还是通过,然后退一步问问自己,那些测试是否能够最大化你投资的成果

测试代码量会比被测试代码还要多

目前为止,我们的编码库已经纵横 190 个文件和 18,000 行代码,达到了 544 kB。我们测试部分的代码现在差不多有1,200 kB,大概有被测试代码的两倍。虽然我们还没有完全结束这个项目,但是已经接近尾声。


引用文章
Real-World Testing with XCTest
Bad Testing Practices

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/8749.html

相关文章

  • 使用macaca进行移动端hybird自动化测试(二)

    摘要:正是因为这样的协议存在一些自动化测试框架可以使用多种语言编写测试脚本。支持了所有的主流浏览器,同时还支持了和的移动应用测试。接下来就带着大家一步一步使用进行进行自动化测试全局安装的如果觉得比较慢,就换淘宝的镜像吧。 Macaca macaca是阿里开源的基于Node.js开发的自动化测试工具,支持native,hybird,moblie web,关于macaca具体的内容参见官网mac...

    wzyplus 评论0 收藏0
  • PerfDog性能狗上手体验及总结分析

    摘要:,部分设备支持功能,在启用功能后,系统会对内存进行压缩,增加,会相应减少,由于压缩会占用资源,同时相应会导致降低虚拟内存整机可用剩余内存,极限测试情况下开启可能会造成性能损耗。 一、PerfDog简介 PerfDog性能狗是移动全平台iOS/Android性能测试工具,快速定位分析性能问题,...

    xiangzhihong 评论0 收藏0
  • 在github规范开发以及持续构建php项目

    摘要:本文目的是通过自己写的一个的简单的库花密密码生成工具,来学习我认为的库开发的一些规范,以及上持续构建你的项目的一些方法。给自己的项目开启持续构建。包发布以及当你的代码完成后,测试完成后。 本文目的是通过自己写的一个php的简单的库(花密密码生成工具), 来学习我认为的php库开发的一些规范,以及github上持续构建你的项目的一些方法。其实是为了显示下边一系列的的徽章 showImg(...

    Sleepy 评论0 收藏0
  • CI Weekly #4 | 不同规模的团队,如何做好持续集成?

    摘要:王者荣耀项目组高级测试工程师工程师文化团队中的实践本文不是一篇入门教程,而是从结合实际场景,阐述在团队协作中如何去好好地应用。 CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成、持续交付,持续部署、自动化测试、 DevOps 等实践教程、工具与资源,以及一些工程师文化相关的程序员 Tips 。同步于 flow.ci Blog、微信公众号、官方微...

    jeffrey_up 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<