Перейти к основному содержимому
Модуль 9 · Урок 1 из 541/50 в курсе~12 min
Уроки модуля (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.
Go
// 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)
    }
}

Запустите его с помощью:

Bash
go test ./...        # all packages in the module
go test -v ./pkg     # verbose: prints test names
go test -run TestSum  # only tests matching the regex

t.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, что позволяет увидеть все сбои за один запуск.

Go
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:

Go
func TestDB(t *testing.T) {
    db := newTestDB(t)
    t.Cleanup(func() { db.Close() })
    // ... test
}

Преимущество перед defer: помощник, создающий db, может зарегистрировать очистку внутренне, поэтому вызывающим объектам не нужно ее запоминать.

Помощники по тестированию

Метод t.Helper() помечает функцию как «вспомогательную»: если тест не пройден, трассировка стека пропускает вспомогательную функцию и указывает непосредственно на вызывающую функцию:

Go
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
}

Упражнения

Упражнение#go.m9.l1.e1
Попыток: 0Загрузка…

Определите функцию TestSum, которая проверяет Sum(2,3) == 5, используя t.Errorf с форматированным сообщением.

Загрузка редактора…

Решение доступно после 3 попыток

Упражнение#go.m9.l1.e2
Попыток: 0Загрузка…

В TestDiv используйте t.Fatal, чтобы немедленно остановить тест, если Div возвращает неожиданное значение.

Загрузка редактора…

Решение доступно после 3 попыток

Quiz#go.m9.l1.e3
Готово

В чем принципиальная разница между t.Error и t.Fatal?

Go
if err != nil {
  t.???(err)
}
Варианты ответа