测试驱动开发

尝试使用 golang 实现 TDD 示例

1. 多币种资金

资金报表

票据 股票 股价 合计
IBM 1000 25 美元 25000 美元
Novartis 400 150 瑞士法郎 60000 瑞士法郎
总计 65000 美元

汇率表

源币种 兑换币种 汇率
瑞士法郎 美元 1.5

Gherkin 表示

# language: zh-CN

  功能:计算总计金额
    根据给定的初始金额及倍率计算金额

    场景:计算合计金额
      假设报表提供公司
      * 股份
      * 股价
      * 币种
      当报表股份乘以股价
      那么报表得到各条目合计金额
      
    场景:合计金额兑换
      假设用户指定使用美元作为兑换币种
      * 提供各币种汇率
      当报表源币种乘以汇率
      那么报表得到各条目兑换币种后的合计金额

    场景:计算总计金额
      假设报表提供各条目兑换币种后的合计金额
      当报表合计金额相加
      那么报表得到总计金额

计划清单(to-do list)

当瑞士法郎与美元的兑换率为 2:1 的时候,5 美元 + 10 瑞士法郎 = 10 美元

5 美元 * 2 = 10 美元

???? 测试先行

乘法功能的简单实例:

func TestMultiplication(t *testing.T){
	dollar := Dollar{5}
	dollar.times(2)

	got := dollar.amount
	want := 10

	assertEquals(t,got,want)
}

func assertEquals(t *testing.T, got,want int) {
	if got != want {
		t.Errorf("got %v want %v",got, want)
	}
}

???? 小步快跑

存在一个编译错误:

  • 未定义 Dollar

最小的改动让编译通过

  1. 我们通过定义一个 Dollar 结构体来去掉一个错误:
type Dollar struct {
}

编译未通过,新增以下编译错误:

  • Dollar 结构体的值太多

  • dollar.times 未定义

  • dollar.amount 未定义

最小的改动让编译通过

  1. 为 Dollar 结构体新增字段 amount:
type Dollar struct {
	amount int
}
  1. 为 Dollar 定义 times 方法
func(d *Dollar) times() {
}

编译未通过,新增以下编译错误:

  • 调用 dollar.times 时的参数太多

最小改动让编译通过

  1. 为 times 方法增加形参
func(d *Dollar) times(multiplier int) {
}

编译通过,但 got = 5 而期望 want = 10

最小改动让测试通过

  1. 通过 times 方法为 amount 赋值
func(d *Dollar) times(multiplier int) {
	d.amount = 10
}

???? 代码重构

去除测试中的数据与代码中的数据间的重复设计

func(d *Dollar) times(multiplier int) {
    d.amount *= multiplier
}

计划清单(to-do list)

当瑞士法郎与美元的兑换率为 2:1 的时候,5 美元 + 10 瑞士法郎 = 10 美元

5 美元 * 2 = 10 美元

完成计划清单 10 美元后的完整代码:

测试代码

func TestMultiplication(t *testing.T){
	dollar := Dollar{5}
	dollar.times(2)

	got := dollar.amount
	want := 10

	assertEquals(t,got,want)
}

func assertEquals(t *testing.T, got,want int) {
	if got != want {
		t.Errorf("got %v want %v",got, want)
	}
}

实现代码

type Dollar struct {
	amount int
}

func(d *Dollar) times(multiplier int) {
	d.amount *= multiplier
}