Hello, YOU
书接上文👈上篇文章中,我们尝试编写了hello word 函数以及第一个测试 hello_test
package main import "fmt" func Hello() string { return "Hello, world" } func main() { fmt.Println(Hello()) }
package main import "testing" func TestHello(t *testing.T) { got := Hello() want := "Hello, world" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }
现在有了测试,就可以安全地迭代我们的软件了。
在上一个示例中,我们在写好代码 之后 编写了测试,以便让你学会如何编写测试和声明函数。
从此刻起,我们将 首先编写测试。
我们的下一个需求是指定 hello 问候的接受者。
让我们从在测试中捕获这些需求开始。这是基本的测试驱动开发,可以确保我们的测试用例 真正 在测试我们想要的功能。
当你回顾编写的测试时,存在一个风险:即使代码没有按照预期工作,测试也可能继续通过。
package main import "testing" func TestHello(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }
这时运行 go test
,你应该会获得一个编译错误
当使用像 Go 这样的静态类型语言时,聆听编译器 是很重要的。
编译器理解你的代码应该如何拼接到一起工作,所以你就不必关心这些了。
在这种情况下,编译器告诉你需要怎么做才能继续。我们必须修改函数
Hello
来接受一个参数。
修改
Hello
func Hello(name string) string { return "Hello, world" }
如果你尝试再次运行测试,main.go
会编译失败,因为你没有传递参数。传入参数「world」让它通过。
func main() { fmt.Println(Hello("world")) }
现在,当你运行测试时,你应该看到类似的内容
我们最终得到了一个可编译的程序,但是根据测试它并没有达到我们的要求。
为了使测试通过,我们使用
name
参数并用 Hello,字符串连接它
func Hello(name string) string { return "Hello, " + name }
现在再运行测试应该就通过了。
接下来我们可以介绍一下另一种语言特性 常量。
通常我们这样定义一个常量
const englishHelloPrefix = "Hello, "
现在我们可以重构代码
const englishHelloPrefix = "Hello, " func Hello(name string) string { return englishHelloPrefix + name }
重构之后,重新测试以确保程序无误。
常量应该可以提高应用程序的性能,它避免了每次使用
Hello
时创建 "Hello, "
字符串实例。显然,对于这个例子来说,性能提升是微不足道的!但是创建常量的价值是可以快速理解值的含义,有时还可以帮助提高性能。
再次回到 Hello, world
下一个需求是当我们的函数用空字符串调用时,它默认为打印 “Hello, World” 而不是 “Hello, “
首先编写一个新的失败测试
func TestHello(t *testing.T) { t.Run("saying hello to people", func(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }) t.Run("say hello world when an empty string is supplied", func(t *testing.T) { got := Hello("") want := "Hello, World" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }) }
这里我们将介绍测试库中的另一个工具 — 子测试。有时,对一个「事情」进行分组测试,然后再对不同场景进行子测试非常有效。
这种方法的好处是,你可以建立在其他测试中也能够使用的共享代码。
当我们检查信息是否符合预期时,会有重复的代码。
重构不 仅仅 是针对程序的代码!
重要的是,你的测试 清楚地说明 了代码需要做什么。
func TestHello(t *testing.T) { assertCorrectMessage := func(t *testing.T, got, want string) { t.Helper() if got != want { t.Errorf("got '%q' want '%q'", got, want) } } t.Run("saying hello to people", func(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" assertCorrectMessage(t, got, want) }) t.Run("empty string defaults to 'world'", func(t *testing.T) { got := Hello("") want := "Hello, World" assertCorrectMessage(t, got, want) }) }
我们在这里做了什么?
我们将断言重构为函数。这减少了重复并且提高了测试的可读性。
在 Go 中,你可以在其他函数中声明函数并将它们分配给变量。
你可以像调用普通函数一样调用它们。我们需要传入
t *testing.T
,这样我们就可以在需要的时候令测试代码失败。t.Helper()
需要告诉测试套件这个方法是辅助函数(helper)。通过这样做,当测试失败时所报告的行号将在函数调用中而不是在辅助函数内部。这将帮助其他开发人员更容易地跟踪问题。
如果你仍然不理解,请注释掉它,使测试失败并观察测试输出。
现在我们有了一个很好的失败测试,所以让我们使用
if 修复代码。
const englishHelloPrefix = "Hello, " func Hello(name string) string { if name == "" { name = "World" } return englishHelloPrefix + name }
如果我们运行测试,应该看到它满足了新的要求,并且我们没有意外地破坏其他功能。
总结
让我们再次回顾一下这个周期
-
编写一个测试
-
让编译通过
-
运行测试,查看失败原因并检查错误消息是很有意义的
-
编写足够的代码以使测试通过
-
重构
从表面上看可能很乏味,但坚持这种反馈循环非常重要。
它不仅确保你有 相关的测试,还可以确保你通过重构测试的安全性来 设计优秀的软件。
查看测试失败是一个重要的检查手段,因为它还可以让你看到错误信息。作为一名开发人员,如果测试失败时不能清楚地说明问题所在,那么使用这个代码库可能会非常困难。
通过确保你的测试的效率 ,并设置你的工具,可以使运行测试足够简单,你在编写代码时就可以进入流畅的状态。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net