TDD
在我看来,使用TDD的一个好处是避免在程序中加入无关的代码。参考一个TDD的定义和TDD的开发feature的流程如下:
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
因此也就意味着当我们使用TDD的方式进行开发的时候,我们要首先将功能表述为代码的行为或者测试案例,然后将测试案例落实。此时我们的代码是无法通过测试的(红)。然后我们只需要写必要的代码使得测试通过(绿)。
从而我们保证了:
- 所编写的测试案例是最小的
- 所编写的代码是最小的
- 所有功能是通过的
测试的角度
我们接下来参考一个计算器实现来说举例一下。
范围 | 处理方式 |
---|---|
接口 | unit test |
模拟使用 | binary test |
集成 | integration test |
Unit Test
对于指定功能如sum(a,b)
我们可以在接口层面进行测试。
test_sum
1,1,2, true
1,2,3, true
2,3,4, false // 错误/异常捕捉
如果可以,尽量包含错误/异常捕捉。
Binary Test 与 Mock
在这个层面,考虑用户如何使用我们的程序,比如
$./calculator sum 1 2
3
因此测试的结构为:
before() {
compile() // binary
}
test_sum(){
// go_exec.binary.arg1,arg2,arg3
}
after() {
cleanup()
}
Integration Test 与 CI
这时候我们考虑到需求变更,需要一个什么交互方式。比如把乘法计算放在服务器上跑,从而计费(每算一次收费xxx)
因此测试的结构为:
before() {
start_server() //set up server with a specific version
compile() // binary
}
test_sum(){
// go_exec.binary.arg1,arg2,arg3
}
after() {
cleanup()
}
但是我们又不是很想把服务器安装设置的过程放在代码中,因此可以考虑通过ci环境来满足这部分的测试需求,逻辑上(伪代码)如下
before() {
if mock {
compile_mock() //set up server with a specific version
start_mock()
}
compile() // binary
}
test_sum(){
// go_exec.binary.arg1,arg2,arg3
}
after() {
cleanup()
}
技巧:我们可以把测试代码中目标server的地址写为127.0.0.1:xxx
这时,无论是在继承测试环境,还是在mock环境下测试。都可以指向这个地址进行通讯,而mock只在mock过程中使用。从而实现测试代码复用。
参考链接
https://www.ibm.com/garage/method/practices/code/practice_test_driven_development
http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd