运行 go test 后编译器会提示失败信息 ./dictionary_test.go:8:9: undefined: Search。
编写最少量的代码让测试运行并检查输出
在 dictionary.go 中:
packagemainfuncSearch(dictionary map[string]string, word string) string {return""}
测试应该失败并显示明确的错误信息:
dictionary_test.go:12: got '' want 'this is just a test' given, 'test'。
编写足够的代码使测试通过
funcSearch(dictionary map[string]string, word string) string {return dictionary[word]}
从 map 中获取值和数组相同,都是通过 map[key] 的方式。
重构
funcTestSearch(t *testing.T) { dictionary :=map[string]string{"test": "this is just a test"} got :=Search(dictionary, "test") want :="this is just a test"assertStrings(t, got, want)}funcassertStrings(t *testing.T, got, want string) { t.Helper()if got != want { t.Errorf("got '%s' want '%s'", got, want) }}
我决定创建一个 assertStrings 辅助函数并删除 given 的部分让实现更通用。
使用自定义的类型
我们可以通过为 map 创建新的类型并使用 Search 方法改进字典的使用。
在 dictionary_test.go 中:
funcTestSearch(t *testing.T) { dictionary :=Dictionary{"test": "this is just a test"} got := dictionary.Search("test") want :="this is just a test"assertStrings(t, got, want)}
funcTestSearch(t *testing.T) { dictionary :=Dictionary{"test": "this is just a test"} t.Run("known word", func(t *testing.T) { got, _ := dictionary.Search("test") want :="this is just a test"assertStrings(t, got, want) }) t.Run("unknown word", func(t *testing.T) { _, err := dictionary.Search("unknown") want :="could not find the word you were looking for"if err ==nil { t.Fatal("expected to get an error.") }assertStrings(t, err.Error(), want) })}
在 Go 中处理这种情况的方法是返回第二个参数,它是一个 Error 类型。
Error 类型可以使用 .Error() 方法转换为字符串,我们将其传递给断言时会执行此操作。我们也用 if 来保护 assertStrings,以确保我们不在 nil 上调用 .Error()。
尝试运行测试
这不会通过编译
./dictionary_test.go:18:10: assignment mismatch: 2 variables but 1 values
func (d Dictionary) Search(word string) (string, error) { definition, ok := d[word]if!ok {return"", errors.New("could not find the word you were looking for") }return definition, nil}
var ErrNotFound = errors.New("could not find the word you were looking for")func (d Dictionary) Search(word string) (string, error) { definition, ok := d[word]if!ok {return"", ErrNotFound }return definition, nil}
funcTestAdd(t *testing.T) { t.Run("new word", func(t *testing.T) { dictionary :=Dictionary{} word :="test" definition :="this is just a test" err := dictionary.Add(word, definition)assertError(t, err, nil)assertDefinition(t, dictionary, word, definition) }) t.Run("existing word", func(t *testing.T) { word :="test" definition :="this is just a test" dictionary :=Dictionary{word: definition} err := dictionary.Add(word, "new test")assertError(t, err, ErrWordExists)assertDefinition(t, dictionary, word, definition) })}
./dictionary_test.go:30:13: dictionary.Add(word, definition) used as value
./dictionary_test.go:41:13: dictionary.Add(word, "new test") used as value
编写最少量的代码让测试运行并检查输出
在 dictionary.go 中:
var ( ErrNotFound = errors.New("could not find the word you were looking for") ErrWordExists = errors.New("cannot add word because it already exists"))func (d Dictionary) Add(word, definition string) error { d[word] = definitionreturnnil}
现在我们又得到两个错误。我们仍在修改值,并返回 nil 错误。
dictionary_test.go:43: got error '%!s(<nil>)' want 'cannot add word because it already exists'
dictionary_test.go:44: got 'new test' want 'this is just a test'
const ( ErrNotFound =DictionaryErr("could not find the word you were looking for") ErrWordExists =DictionaryErr("cannot add word because it already exists"))typeDictionaryErrstringfunc (e DictionaryErr) Error() string {returnstring(e)}
我们将错误声明为常量,这需要我们创建自己的 DictionaryErr 类型来实现 error 接口。你可以在 Dave Cheney 的这篇优秀文章中了解更多相关的细节。简而言之,它使错误更具可重用性和不可变性。
首先编写测试
funcTestUpdate(t *testing.T) { word :="test" definition :="this is just a test" dictionary :=Dictionary{word: definition} newDefinition :="new definition" dictionary.Update(word, newDefinition)assertDefinition(t, dictionary, word, newDefinition)}
Update 与 Create 密切相关,这是下一个需要我们实现的方法。
尝试运行测试
./dictionary_test.go:53:2: dictionary.Update undefined (type Dictionary has no field or method Update)
t.Run("existing word", func(t *testing.T) { word :="test" definition :="this is just a test" newDefinition :="new definition" dictionary :=Dictionary{word: definition} err := dictionary.Update(word, newDefinition)assertError(t, err, nil)assertDefinition(t, dictionary, word, newDefinition)})t.Run("new word", func(t *testing.T) { word :="test" definition :="this is just a test" dictionary :=Dictionary{} err := dictionary.Update(word, definition)assertError(t, err, ErrWordDoesNotExist)})
我们在单词不存在时添加了另一种错误类型。我们还修改了 Update 以返回 error 值。
尝试运行测试
./dictionary_test.go:53:16: dictionary.Update(word, "new test") used as value
./dictionary_test.go:64:16: dictionary.Update(word, definition) used as value
./dictionary_test.go:66:23: undefined: ErrWordDoesNotExists
这次我们得到 3 个错误,但我们知道如何处理这些错误。
编写最少量的代码让测试运行并检查输出
const ( ErrNotFound =DictionaryErr("could not find the word you were looking for") ErrWordExists =DictionaryErr("cannot add word because it already exists") ErrWordDoesNotExist =DictionaryErr("cannot update word because it does not exist"))func (d Dictionary) Update(word, definition string) error { d[word] = definitionreturnnil}
我们添加了自己的错误类型并返回 nil 错误。
通过这些更改,我们现在得到一个非常明确的错误:
dictionary_test.go:66: got error '%!s(<nil>)' want 'cannot update word because it does not exist'