Go: Автоматическое тестирование

Теория: Покрытие кода и go test -cover

Когда разработчик пишет тесты, естественный вопрос — «а действительно ли мой код проверен полностью?». Может оказаться, что часть функций никогда не вызывается из тестов. В таком случае ошибки в этих местах останутся незамеченными.

Чтобы оценить полноту тестов, используют покрытие кода (coverage). В Go этот инструмент встроен прямо в стандартную систему тестирования — достаточно запустить go test с нужными флагами.

Что такое покрытие

Покрытие показывает, сколько строк программы было выполнено во время запуска тестов. Если функция была вызвана, значит её строки засчитаны в покрытие. Если нет — они остаются «красными» в отчёте.

Важно понимать: покрытие отвечает на вопрос «был ли этот код выполнен во время тестов», но не говорит «проверялся ли результат правильно». То есть можно вызвать функцию и ничего не проверить — покрытие вырастет, но пользы от такого теста мало.

Первый пример: простой модуль

Допустим, есть пакет calc с двумя функциями:

package calc

func Add(a, b int) int {
	return a + b
}

func Sub(a, b int) int {
	return a - b
}

И есть тест, который проверяет только сложение:

package calc_test

import (
	"calc"
	"testing"
)

func TestAdd(t *testing.T) {
	got := calc.Add(2, 2)
	want := 4
	if got != want {
		t.Errorf("Add(2, 2) = %d, хотели %d", got, want)
	}
}

Теперь запускаем тест с покрытием:

go test -cover

Вывод:

ok  	calc	0.003s	coverage: 50.0% of statements

Go говорит: покрытие 50%. Это значит, что половина строк кода была выполнена тестами, а вторая половина (функция Sub) вообще не вызывалась.

Как увидеть подробнее

Флаг -cover показывает только общий процент. Но часто важно понять, какая именно функция осталась без теста. Для этого используют флаг -coverprofile.

go test -coverprofile=coverage.out

Теперь результаты записаны в файл coverage.out. Внутри хранится карта: какие строки выполнялись, а какие нет. Этот файл можно разобрать с помощью утилиты go tool cover.

Например:

go tool cover -func=coverage.out

Результат:

calc/calc.go:3: Add 100.0% calc/calc.go:7: Sub 0.0% total: (statements) 50.0%

Теперь ясно: функция Add покрыта полностью, а Sub — нет.

Визуальный отчёт

Чтобы было нагляднее, можно открыть отчёт в браузере. Для этого используется команда:

go tool cover -html=coverage.out

Go сгенерирует HTML-страницу: строки, которые выполнялись, будут подсвечены зелёным, а строки, до которых тесты не добрались, — красным. Это самый удобный способ увидеть, где именно остаются пробелы.

Добавим тест

Напишем проверку для Sub:

func TestSub(t *testing.T) {
	got := calc.Sub(5, 3)
	want := 2
	if got != want {
		t.Errorf("Sub(5, 3) = %d, хотели %d", got, want)
	}
}

Запустим снова:

go test -cover

Теперь:

ok  	calc	0.004s	coverage: 100.0% of statements

А если открыть go tool cover -html=coverage.out, все строки будут зелёными.

Как это выглядит в больших проектах

В реальном коде тесты редко покрывают всё на 100%. Всегда остаются места, которые проверять сложно: ветки с ошибками, защитные условия, редко используемые сценарии.

Обычно нормальным считается уровень от 70 до 90 процентов. Стремиться к 100% имеет смысл только в библиотеке с критичной логикой (например, криптография).

Практический сценарий

Например, есть функция:

func Divide(a, b int) (int, error) {
	if b == 0 {
		return 0, fmt.Errorf("division by zero")
	}
	return a / b, nil
}

Если тесты проверяют только корректное деление, то ветка с ошибкой остаётся красной.

func TestDivide_OK(t *testing.T) {
	got, err := Divide(10, 2)
	if err != nil {
		t.Fatal(err)
	}
	if got != 5 {
		t.Errorf("ожидали 5, получили %d", got)
	}
}

Запускаем с покрытием — и видим, что часть кода не проверена. Нужно добавить тест на ошибочный сценарий:

func TestDivide_Error(t *testing.T) {
	_, err := Divide(10, 0)
	if err == nil {
		t.Fatal("ожидали ошибку, но получили nil")
	}
}

Теперь покрытие станет 100%, и в HTML-отчёте обе ветки будут зелёными.

Покрытие кода тестами показывает, какие строки программы реально выполняются при прогоне тестов. В Go для этого встроен инструмент: go test -cover выводит общий процент, -coverprofile сохраняет подробности в файл, а go tool cover -html=coverage.out позволяет открыть отчёт в браузере.

Это удобный способ видеть, где тесты отсутствуют совсем. Но покрытие не заменяет здравый смысл: можно достичь 100%, но при этом тесты будут проверять только «что функция вызвалась», а не правильность результата. Полезность тестов определяется не только числом зелёных строк, но и тем, насколько они действительно проверяют логику программы.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845