swift中的单元测试

680 阅读7分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

什么是单元测试

单元测试是一种软件测试方法,通过该方法测试各个单位的源代码,一个或多个计算机程序模块的组合以及关联的控制数据,使用和操作程序,以确定它们是否正常运行。

单元测试的来源

单元测试源于工业生产,大多数人都忘记了这一点。在工业上,我们需要测试每一个小部件,看它的质量是否合格,我们是否可以把它用在下一步的部件装配中。在软件行业,我们没有这样清晰的单元定义;人们有不同的看法——方法、类、模块。第一个难点是让所有的团队成员对单元是什么有一个共识,并编写相应的测试。在我看来,单元非常小,就像发动机中的螺丝或者家具中的钉子那么大。所以,我建议定义单元是什么,并在测试审核阶段对此进行密切关注,确保其得到了执行。

单元测试混合了编程和测试。为了编写简单、快捷、可维护的测试,程序员需要知道许多测试概念。我对程序员的建议是从负责测试的同事那里学习一些基本的概念:等价划分、边界值分析、测试覆盖、正向测试、逆向测试。

关于单元测试,还有另外一个经常被遗忘的部分,那就是分析。我们不能不经过思考就立即开始编写测试。我们需要分析问题,如果可能对它进行划分,然后再考虑我们需要编写的单元测试。这里有一个不错的建议,就是总是从系统输出开始,然后找出生成那个输出的可能输入。感谢 Chris Matts 把这个技巧教给了我。

在编写单元测试时,我们应该使用来自问题领域的词汇。测试应该体现一项特性存在于产品中的理由。一个了解业务领域但不了解编程的人也应该能够阅读你的测试。在那方面,和分析师结对非常有用。

为什么要进行单元测试?

单元测试是为了避免你的app变成充满bug的软件,让我们在开发过程中能更好的发现缺陷,提高代码质量,也能保证在代码重构时及时发现改动带来的问题。

单元测试应该测什么?

  • 核心功能:模型类和方法,以及它们和控制器的交互

  • 最常用的UI操作

  • 边际条件

  • bug修复

单元测试需要遵循的原则是什么?

FIRST原则--测试的最佳实践,遵循FIRST原则会让你的测试更加清晰有用

Fast:测试的运行速度要快,这样人们就不介意你运行它们

Independent/Isolated:一个测试不应当依赖于另一个测试

Repeatable:同一个测试,每次都应该获得相同的结果。外部数据提供者和并发问题会导致间歇性的出错

Self-validating:测试应当是完全自动化的,输出结果要么是pass要么是fail,而不是依靠程序员地日志文件的解释

Timely:理想情况下,测试的编写,应当在编写要测试的产品代码之前

什么是TDD?

TDD是 Test Drive Development,指的是测试驱动开发,相对于普通思维模式来说,是一种比较极端的做法我们一般都是先编写产品代码,在写测试代码,而TDD恰好相反,其思想是先写测试代码,然后再编写相应的产品代码。

TDD一般遵循 red->green->refactor 的步骤,即(报错->通过->重构),因为是先写了测试代码,而还未添加产品代码,所以编译器会给出红色报警,当把相应的产品代码添加完善后,并让单元测试用例通过测试,通过的状态为绿色,如此反复直到各种边界和测试都进行完毕,此时我们就可以得到一个稳定的产品,所以可以大胆的对产品代码进行重构,只要保证项目最后是绿色状态,就说嘛重构的代码没问题。

TDD的过程,类似于脚本语言的交互式编程,写几行代码,就可以检查运行结果,如果结果有误,则要把最近的代码充血,知道单元测试结果正确为止。

什么是BDD?

BDD是Behavior Drive Development ,指的是行为驱动开发,常用于敏捷开发中使用的测试方法,其主要是为了解决XCTest苹果官方测试框架测试时难以mock和stub的问题。

BDD提倡使用Given...When...Then 这种类似自然语言的描述来编写测试代码,在objc中,现在比较流行的BDD框架有specta、Kiwi、ceder,github上start较多的是Kiwi,在swift中,专用的 BDD 测试框架是Quick和Sleipnir。

什么是Stub

Stub是指人为地让一个对象对某个方法返回我们事先规定好的值。

Stub运用的主要场景是你需要和别的开发人员协同开发时,别人的模块尚未完成,而你的模块需要用到别人的模块,这时就需要Stub。例如,后端的接口未完成,你的代码已经完成了,Stub可以伪造一个调用的返回。

ojbc下可以使用OHHTTPStubs来伪造网络的数据返回。swift下,仍要手动写stub。

什么是Mock?

Mock是一个非常容易和stub混淆的概念,简单来说,我们可以将Mock看做是一种更全面和更智能的Stub。

明确来说,Mock其实是一个对象,它是对现有类行为的一种模拟(或是岁现有接口实现的模拟)。

Mock和Stub最大的区别在于Stub只是简单的方法替换,不涉及新的对象,被stub的对象可以是业务代码中真正的对象,而Mock行为本身产生新的(不可能在业务代码中出现)的对象,并遵循类的定义响应某些方法。

Mock让你可以检查某种情况下,一个方法是否被调用,或者一个属性是否被正确设值。objc下可以使用OCMock来mock对象。但是,由于swift的runtime比较弱,所以,swift上一般要手动写mock

单元测试的方法


XCTFail(format…) 生成一个失败的测试;

XCTAssertNil(a1, format...)为空判断,a1为空时通过,反之不通过;

XCTAssertNotNil(a1, format…)不为空判断,a1不为空时通过,反之不通过;

XCTAssert(expression, format...)当expression求值为TRUE时通过;

XCTAssertTrue(expression, format...)当expression求值为TRUE时通过;

XCTAssertFalse(expression, format...)当expression求值为False时通过;

XCTAssertEqualObjects(a1, a2, format...)判断相等,[a1 isEqual:a2]值为TRUE时通过,其中一个不为空时,不通过;

XCTAssertNotEqualObjects(a1, a2, format...)判断不等,[a1 isEqual:a2]值为False时通过;

XCTAssertEqual(a1, a2, format...)判断相等(当a1和a2是 C语言标量、结构体或联合体时使用, 判断的是变量的地址,如果地址相同则返回TRUE,否则返回NO);

XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);

XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试;

XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试;

XCTAssertThrows(expression, format...)异常测试,当expression发生异常时通过;反之不通过;(很变态) XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;

XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过;

XCTAssertNoThrow(expression, format…)异常测试,当expression没有发生异常时通过测试;

XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;

XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过