译:如何用Swift进行TDD(测试驱动开发)

如果你还没有用类似Swift的编译型语言进行过TDD,你可能想问:如果测试引用的对象不存在,你怎么进行代码编译,又怎么进行TDD呢?

相对于类似Swift的编译型语言,类似Ruby和JavaScript的解释型语言可能天生更适合TDD,因为你可以编写不存在的测试对象,并且不会产生编译错误。

所以该如何用编译型语言进行TDD?

你可以直接编写测试代码,放任它编译失败。如果你把“编译失败”当作解释型语言的测试失败,就简单多了。失败就是失败,无论是由于编译器还是你的测试。

为了说明这一点,我们对Project类进行TDD,我们希望创建一个它的字典,这样之后可以进行序列化。

1、创建一个测试和你想要存在的实例

因为我们想要测试的是创建一个Project的字典,我们需要一个Project的实例(当前它并不存在)。

class ProjectTests: XCTestCase { func test_asDictionary() { let project = Project(id: 5) } } 编译失败,所以测试失败了。我们有一个好的开始,说真的,这就是TDD——我们希望我们的第一个测试是失败的。

测试状态:红色。

2、编写你想要存在的类

为了解决编译错误,Project需要一个有id参数的init,代码如下:

class Project { private let id: Int

init(id: Int) {
    self.id = id
}

} 这修复了编译错误,所以测试通过。

测试状态:绿色。

3、在测试中,调用你想要存在的方法

现在我们想用Project实例调用asDictionary方法,这个方法将给我们Project的字典表示。

func test_asDictionary() { let project = Project(id: 5) let dict = project.asDictionary() } 编译失败,所以测试状态为红色。好,我们可以继续进行。

测试状态:红色。

4、编写你想要存在的方法

在Project类里,我们现在可以实现asDictionary方法了,但是注意我们要用最简短的代码来通过测试。(换句话说,不要用的id属性!)

func asDictionary() -> [String: AnyObject] { return String: AnyObject } 记住,在TDD过程中,我们总是试图做最简单的事情来通过测试。所以这里我们只返回一个空的字典——我们暂时不需要任何键或值,因为没有失败的测试告诉我们这样做。

这使得测试状态为绿色,因为它修复了编译错误。当然,我们的测试还不告诉我们很多信息,所以我们需要写一个断言。

测试状态:绿色。

5、在测试里,编写一个断言

现在我们可以在asDictionary方法的返回值里做断言。我们希望Project的id出现在字典里。所以我们的测试变成了这样:

func test_asDictionary() { let project = Project(id: 5) let dict = project.asDictionary()

XCTAssertEqual(dict["id"] as? Int, 5)

} 这通过了编译,但是运行的时候,测试失败了,它告诉我们nil并不等于5。我们的测试再次失败,但没关系,我们可以修复它!

测试状态:红色。

6、实现方法,来通过测试

现在我们可以编写方法逻辑,履行断言,使测试通过。

回到我们的Project,我们可以更新asDictionary:

func asDictionary() -> [String: AnyObject] { return ["id": 5] } 什么?你可能会想,现在我们不是应该返回id而不是5吗?如果我们真的在实行TDD,那就不应该,我们不应该返回id属性的值。返回硬编码值5在这里是最简单的通过测试的方法。如果我们想断言返回的字典里有id,我们需要另一个测试。

测试状态:绿色。断言状态:不够好。

7、编写另一个测试,下一个新的断言

现在我们可以编写一个完整的测试,并且没有任何编译错误。我们会创建一个新的测试,其中Project的id能给出除5以外的一个值,调用asDictionary,下断言。

func test_asDictionary_with_id_7() { let project = Project(id: 7) let dict = project.asDictionary()

XCTAssertEqual(dict["id"] as? Int, 7)

} 这将会编译失败,因为asDictionary的id值总是5。这很好,因为现在我们有一些不错的断言告诉我们代码应该如何工作。

测试状态:红色。断言状态:好。

8、实现方法,使测试通过

现在我们可以更新asDictionary使我们的测试通过。但是这一次,返回一个硬编码["id": 7]并没有用,因为这将打破我们的第一个测试。我们可以修改这个方法来返回字典里的id值,像这样:

func asDictionary() -> [String: AnyObject] { return ["id": id] } 当我们运行测试,他们通过了!现在我们可以相信asDictionary将始终返回字典里的id。

测试状态:绿色。断言状态:好。

结论

你可以用类似Swift的编译型语言实践TDD——事实上, Test Driven Development: By Example(这本书继续谈了TDD)使用了Java这个编译型语言来说明如何进行TDD。只要你以同样的方式对待编译错误和解释型语言的测试失败,TDD过程是完全相同的。

http://www.cocoachina.com/swift/20151112/14152.html

翻译:http://roadfiresoftware.com/2015/09/how-you-can-do-tdd-with-swift/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏信数据得永生

JavaScript 编程精解 中文第三版 八、Bug 和错误

31610
来自专栏别先生

Javascript创建对象的学习和使用

1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>javascript对象的学习</title> ...

2039
来自专栏阮一峰的网络日志

回车和换行

今天,我总算搞清楚"回车"(carriage return)和"换行"(line feed)这两个概念的来历和区别了。 在计算机还没有出现之前,有一种叫做电传打...

2995
来自专栏tkokof 的技术,小趣及杂念

Sweet Snippet 系列之 Lua表排序

  作为Lua中实现各类数据结构的基石,表的使用想必是贯穿于整个项目的开发过程之中,其中对表内容的排序想必亦是常见的需求之一,Lua内置的Table函数库便提供...

924
来自专栏Java技术栈

Java 编程中关于异常处理的 10 个最佳实践

异常处理是Java 开发中的一个重要部分。它是关乎每个应用的一个非功能性需求,是为了处理任何错误状况,比如资源不可访问,非法输入,空输入等等。Java提供了...

1704
来自专栏顶级程序员

总算搞清楚了回车和换行的来历与区别

总算搞清楚”回车”(carriage return)和”换行”(line feed)这两个概念的来历和区别了。 在计算机还没有出现之前,有一种叫做电传打字机(...

3055
来自专栏JetpropelledSnake

Linux学习笔记之Redis中5种数据结构的使用场景介绍

原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码。目前目标是吃透 redis 的数据结构。我们都知...

1241
来自专栏撸码那些事

编码最佳实践——开放封闭原则

开放与封闭原则有两种不同的定义,分别是20世纪80年代最原始的定义和后期一个更现代的定义,后者对前者进行更加详尽的阐述。

1393
来自专栏java一日一条

有效处理Java异常的三个原则,你知道吗?

在有效使用异常的情况下,异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出,如果你的异常没有回答以上全部问题,那么可能...

1741
来自专栏编程

java基础思维图解

Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。J...

2199

扫码关注云+社区

领取腾讯云代金券