Go语言:TDD测试驱动开发->Hello, YOU <-通过简单的 hello_test.go 程序学会编写测试,学习使用常量来优化并根据需要重构(谁说hello world就只是 hello world?)
2023-03-29 更新
经过一段时间的学习与实践,针对 Golang 语言基础的 TDD 驱动开发测试训练已经告一段落,接下来会在此基础上继续使用 TDD 来构建应用程序 。
博主前一部分的代码Github先贴下面👇以供参考
https://github.com/slowlydance2me/My_Golang_Study.git
先科普一下 TDD
测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。 它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。 这有助于编写简洁可用和高质量的代码,并加速开发过程。
以下为正文👇
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) } }
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
,你应该会获得一个编译错误
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
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) }) }
![](https://img2023.cnblogs.com/blog/2986763/202303/2986763-20230316110312715-1436670907.png)
t *testing.T
,这样我们就可以在需要的时候令测试代码失败。t.Helper()
需要告诉测试套件这个方法是辅助函数(helper)。![](https://img2023.cnblogs.com/blog/2986763/202303/2986763-20230316110449955-1372177593.png)
if 修复代码。
const englishHelloPrefix = "Hello, " func Hello(name string) string { if name == "" { name = "World" } return englishHelloPrefix + name }
如果我们运行测试,应该看到它满足了新的要求,并且我们没有意外地破坏其他功能。
先来一个小总结
-
编写一个测试
-
让编译通过
-
运行测试,查看失败原因并检查错误消息是很有意义的
-
编写足够的代码以使测试通过
-
重构
继续前进!更多需求
西班牙语
t.Run("in Spanish", func(t *testing.T) { got := Hello("Elodie", "Spanish") want := "Hola, Elodie" assertCorrectMessage(t, got, want) })
先编写测试。当你尝试运行测试时,编译器 应该 会出错,因为你用两个参数而不是一个来调用 Hello
。
./hello_test.go:27:19: too many arguments in call to Hello have (string, string) want (string)
通过向 Hello
添加另一个字符串参数来解决编译问题
func Hello(name string, language string) string { if name == "" { name = "World" } return englishHelloPrefix + name }
当你尝试再次运行测试时,它会报错在其他测试和 hello.go
中没有传递足够的参数给 Hello
函数
./hello.go:15:19: not enough arguments in call to Hello have (string) want (string, string)
通过传递空字符串来解决它们。现在,除了我们的新场景外,你的所有测试都应该编译并通过
hello_test.go:29: got 'Hello, Elodie' want 'Hola, Elodie'
这里我们可以使用 if
检查语言是否是「西班牙语」,如果是就修改信息
func Hello(name string, language string) string { if name == "" { name = "World" } if language == "Spanish" { return "Hola, " + name } return englishHelloPrefix + name }
const spanish = "Spanish" const helloPrefix = "Hello, " const spanishHelloPrefix = "Hola, " func Hello(name string, language string) string { if name == "" { name = "World" } if language == spanish { return spanishHelloPrefix + name } return englishHelloPrefix + name }
法语
-
编写一个测试,断言如果你传递
"French"
你会得到"Bonjour, "
-
看到它失败,检查错误信息是否容易理解
func Hello(name string, language string) string { if name == "" { name = "World" } if language == spanish { return spanishHelloPrefix + name } if language == french { return frenchHelloPrefix + name } return englishHelloPrefix + name }
switch
于是我们自然而然想到了使用 switch
当你有很多 if
语句检查一个特定的值时,通常使用 switch
语句来代替。如果我们希望稍后添加更多的语言支持,我们可以使用 switch
来重构代码,使代码更易于阅读和扩展。
func Hello(name string, language string) string { if name == "" { name = "World" } prefix := englishHelloPrefix switch language { case french: prefix = frenchHelloPrefix case spanish: prefix = spanishHelloPrefix } return prefix + name }
编写一个测试,添加用你选择的语言写的hello,你应该可以看到扩展这个 神奇 的函数是多么简单。
最后一次重构
你可能会抱怨说也许我们的函数正在变得很臃肿。对此最简单的重构是将一些功能提取到另一个函数中。
func Hello(name string, language string) string { if name == "" { name = "World" } return greetingPrefix(language) + name } func greetingPrefix(language string) (prefix string) { switch language { case french: prefix = frenchHelloPrefix case spanish: prefix = spanishHelloPrefix default: prefix = englishHelloPrefix } return }
-
在我们的函数签名中,我们使用了 命名返回值(
prefix string
)。
-
这将在你的函数中创建一个名为
prefix
的变量。 -
它将被分配「零」值。这取决于类型,例如
int
是 0,对于字符串它是""
。 -
你只需调用
return
而不是return prefix
即可返回所设置的值。 -
这将显示在 Go Doc 中,所以它使你的代码更加清晰。
case
语句匹配,将会执行 default
分支。真正的总结
Hello, world
中学到这么多东西呢?-
编写测试
-
用参数和返回类型声明函数
-
if
,const
,switch
-
声明变量和常量
-
编写一个失败的测试,并查看失败信息,我们知道现在有一个为需求编写的 相关 的测试,并且看到它产生了 易于理解的失败描述
-
编写最少量的代码使其通过,以获得可以运行的程序
-
然后 重构,基于我们测试的安全性,以确保我们拥有易于使用的精心编写的代码
Hello()
到 Hello("name")
,到 Hello("name", "french")
。本文来自博客园,作者:slowlydance2me,转载请注明原文链接:https://www.cnblogs.com/slowlydance2me/p/17221349.html