本文由CocoaChina译者 @ALEX吴浩文 翻译
原文:How can you do TDD with Swift?
如果你还没有用类似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过程是完全相同的。