День 14 · дополнительные материалы

День 14 — глубже

Всё здесь необязательно. Материалы на английском помечены.


Официальный разбор первого теста (англ.)

Что печатает go test, если попросить подробностей (англ.)

Сколько строк функции проверил тест

Попробуйте сейчас: покрытие.

Цель: увидеть, какую долю строк вашего кода выполнил тест.

▶ Выполните
cd ~/gocourse/day14/packs
go test -cover .

Число в процентах — доля строк пакета, которые тест выполнил хоть раз. Оно считает строки, а не случаи: тест без единого сравнения даст такое же покрытие, как хороший. Поэтому покрытие — подсказка «куда я вообще не заглядывал», а не оценка качества тестов.

Готово, когда: вы видите строку с процентом и можете сказать, какая ветка packs осталась невыполненной, если процент меньше ста.

Подтесты: имя случая от Go, а не от вас

На странице 5 имя случая приходилось вставлять в сообщение руками — иначе непонятно, какая строка таблицы упала. У testing для этого есть t.Run: он запускает случай как отдельный маленький тест со своим именем.

Код для чтения · разбираем, набирать не нужно
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := price(c.qty)
			if got != c.want {
				t.Errorf("price(%d) = %d, хотели %d", c.qty, got, c.want)
			}
		})
	}

func(t *testing.T) { … } — функция без имени прямо на месте аргумента; такие функции разберём в блоке 5. Зато в выводе каждый случай виден отдельно, и -run умеет выбирать один случай по имени. В курсе до блока 5 пишем цикл без t.Run — форма со страницы 5 работает и понятна целиком.

Как тесты выглядят в самом Go

Исходники стандартной библиотеки лежат на машине рядом с компилятором. Посмотрите, например, тесты пакета strconv:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day14$ ls -1 /usr/local/go/src/strconv/
bytealg.go
bytealg_bootstrap.go
doc.go
example_test.go
export_test.go
import_test.go
isprint.go
makeisprint.go
number.go
number_test.go
quote.go
quote_test.go
strconv_test.go

Файлы _test.go лежат ровно там же, где код, и пишутся по тем же правилам, что ваши. Откройте /usr/local/go/src/strconv/number_test.go в редакторе или через less — это тесты к разбору чисел, там же живёт TestAtoi, тест той самой strconv.Atoi из дня 07.

Внутри — та же таблица случаев, что на странице 5, только записана в две части: сначала описание полей, потом список случаев отдельной переменной.

Код для чтения · разбираем, набирать не нужно
type parseInt64Test struct {
	in  string
	out int64
	err error
}

var parseInt64Tests = []parseInt64Test{
	{"", 0, ErrSyntax},
	{"0", 0, nil},
	{"1", 1, nil},
	{"-1", -1, nil},
	{"12345", 12345, nil},
	{"9223372036854775808", 1<<63 - 1, ErrRange},
	{"123%45", 0, ErrSyntax},
}

Поля те же по смыслу: вход, ожидаемое, ожидаемая ошибка. Случаи — по одному в строке, и среди них ровно то, чему учит страница 4: пустая строка, ноль, граница int64 и строка с посторонним символом. Ходить по таблице TestAtoi тоже будет циклом — for i := range parseInt64Tests.

Мини-опыты

1. Тест на панику. Сделайте функцию, которая делит на количество, и вызовите её с нулём — в тесте. Что напечатает go test? Найдите в выводе слово panic и имя своего теста. Паника валит всю тестовую сборку, а не один тест: остальные тесты после неё не выполнятся.

2. Два теста, один падает. Оставьте в scratch два теста и сломайте ожидаемое в одном. Сколько раз в выводе встретится слово FAIL без -v и с -v? Почему у одного теста будет PASS, а у пакета — FAIL?

3. Кэш. Запустите go test . дважды и убедитесь, что второй раз печатается (cached). Потом допишите в файл теста пустую строку, сохраните и запустите снова — кэш пропал. Почему go test считает, что результат мог измениться?

4. Тест к чужой функции. Возьмите функцию из дня 13 — любую, которую писали не сегодня, — и напишите к ней тест на четыре случая по форме со страницы 5. Сколько случаев придётся придумать, прежде чем тест начнёт ловить подмену тела функции на return 0?