用车
tdd(Go 语言测试驱动开发 (TDD) 实战:倒着跳舞的艺术)

Go 语言测试驱动开发 (TDD) 实战:倒着跳舞的艺术

"Fred Astaire? 当然,他很棒。但别忘了 Ginger Rogers 做了他做的一切……而且是倒着跳的,还穿着高跟鞋。" —— Bob Thaves

这是一篇关于 测试驱动开发 (TDD) 的趣味实战指南。作者 John Arundel(Go 语言作家兼教师)通过一个简单的 IsSorted 函数,展示了如何用"倒着编程"(先写测试,再写代码)的方式,写出健壮、优雅的 Go 代码。

假如函数不存在:一切从测试开始

通常,我们习惯先写函数 IsSorted,再写测试。但这次,我们学学 Ginger Rogers,倒着来

假设 IsSorted 函数已经存在了,我们直接写测试:

func TestIsSorted_IsTrueForSortedSlice(t *testing.T) {    t.Parallel()    input := []int{1, 2, 3}    // 假设 sorted 包和 IsSorted 函数已经存在    if !sorted.IsSorted(input) {        t.Errorf("got false for %v", input)    }}

最快失败路径:红灯 -> 绿灯

现在的代码甚至无法编译,因为 IsSorted 根本不存在。利用 GoLand 的快速修复功能:

  1. 创建 sorted 包。
  2. 创建 IsSorted 函数。
  3. 添加返回值。

为了让测试先跑起来(并确认它能检测到错误),我们故意写一个错误的实现:

func IsSorted(input []int) bool {    return false // 永远返回 false}

运行测试 -> 失败 (FAIL)。太棒了!这证明了我们的测试是有效的。

婴儿学步:逐步完善实现

为了通过测试,我们不能只返回 true(那样无法区分未排序的情况)。我们需要增加反向测试用例:

func TestIsSorted_IsFalseForUnsortedSlice(t *testing.T) {    t.Parallel()    input := []int{1, 3, 2}    if sorted.IsSorted(input) {        t.Errorf("got true for %v", input)    }}

然后,我们写一个"最笨"的实现来通过测试:

func IsSorted(input []int) bool {    prev := 0    for _, v := range input {        if v < prev {            return false        }        prev = v    }    return true}

发现 Bug:倒着修补漏洞

虽然测试通过了,但我们很快意识到这个实现有问题:如果切片包含负数怎么办? 不要急着改代码!先写一个失败的测试用例:

"negative first element": {-1, 2, 3}, // 会失败,因为 -1 < 0 (初始 prev)

看到测试失败后,我们再修改代码,将 prev 初始化为 input[0]。

随后,客户反馈了 Panic:空切片导致崩溃。 同样,先加测试用例 "no elements": {},复现 Panic,然后再修复代码(处理 len < 2 的情况)。

最终重构:标准库的降维打击

代码已经很健壮了,但新同事 Alyssa 指出:标准库 slices 包里已经有 IsSorted 了!

因为我们有完善的测试套件,我们可以放心地进行重构

func IsSorted(input []int) bool {    return slices.IsSorted(input)}

运行测试 -> 全部通过 (PASS)

总结:红、绿、重构

这就是"倒着编程"(TDD)的魅力:

  1. 红 (Red):添加一个失败的测试。
  2. 绿 (Green):写最少的代码让测试通过。
  3. 重构 (Refactor):在测试保护下优化代码。

下次写代码时,不妨试着穿上高跟鞋,倒着跳一段舞?你会发现每一步都踩得无比踏实。


求点赞 求关注 ❤️ 求收藏 ⭐️ 你的支持是我更新的最大动力!


顶一下()     踩一下()

热门推荐

发表评论
0评