Уроки модуля (1/5)
Пакет «Тестирование»
В Go тесты являются частью stdlib: нет внешней библиотеки или фреймворка для настройки. Все, что вам нужно, — это файл *_test.go рядом с кодом, несколько функций TestXxx и команда go test.
Основные соглашения
- Тестовые файлы называются
<something>_test.goи находятся в том же пакете, что и тестируемый ими код (или в пакете<name>_testдля «тестов черного ящика»). - Тестовые функции имеют точную подпись:
func TestXxx(t *testing.T), гдеXxxначинается с заглавной буквы. - Вы импортируете
testingиз стандартной библиотеки. - Нет
assertEquals: вы используетеif got != want { t.Errorf(...) }с глаголамиfmt.
// math.go
package math
func Sum(a, b int) int { return a + b }
// math_test.go
package math
import "testing"
func TestSum(t *testing.T) {
got := Sum(2, 3)
if got != 5 {
t.Errorf("Sum(2,3) = %d; voglio %d", got, 5)
}
}Запустите его с помощью:
go test ./... # all packages in the module
go test -v ./pkg # verbose: prints test names
go test -run TestSum # only tests matching the regext.Error против t.Fatal
Вот два основных глагола, сигнализирующих о неудаче:
| Метод | Эффект |
|---|---|
t.Log(args...) | Регистрируется, но не происходит сбоя (видно только с -v или при сбое). |
t.Error(args...) | Регистрируется, отмечается FAIL, продолжение функции тестирования. |
t.Errorf(fmt, ...) | Как Error с форматированием. |
t.Fatal(args...) | Регистрирует, отмечает FAIL, останавливает тест (вызывает runtime.Goexit). |
t.Fatalf(fmt, ...) | Как Fatal с форматированием. |
Эмпирическое правило: используйте Fatal, когда продолжение не имеет смысла (например, неудачная установка, неизбежное разыменование нулевого указателя); в противном случае Error, что позволяет увидеть все сбои за один запуск.
func TestOpen(t *testing.T) {
f, err := os.Open("fixtures/sample.txt")
if err != nil {
t.Fatal(err) // if it doesn't open, the next steps would fail on nil
}
defer f.Close()
// ... assert on content with t.Errorf
}Настройка и демонтаж с помощью t.Cleanup
Чтобы освободить ресурсы в конце теста (или подтеста), используйте t.Cleanup вместо defer:
func TestDB(t *testing.T) {
db := newTestDB(t)
t.Cleanup(func() { db.Close() })
// ... test
}Преимущество перед defer: помощник, создающий db, может зарегистрировать очистку внутренне, поэтому вызывающим объектам не нужно ее запоминать.
Помощники по тестированию
Метод t.Helper() помечает функцию как «вспомогательную»: если тест не пройден, трассировка стека пропускает вспомогательную функцию и указывает непосредственно на вызывающую функцию:
func mustParseInt(t *testing.T, s string) int {
t.Helper()
n, err := strconv.Atoi(s)
if err != nil {
t.Fatalf("parse %q: %v", s, err)
}
return n
}Упражнения
Определите функцию TestSum, которая проверяет Sum(2,3) == 5, используя t.Errorf с форматированным сообщением.
Решение доступно после 3 попыток
В TestDiv используйте t.Fatal, чтобы немедленно остановить тест, если Div возвращает неожиданное значение.
Решение доступно после 3 попыток
В чем принципиальная разница между t.Error и t.Fatal?
if err != nil {
t.???(err)
}