Avatar ·

Как измерить покрытие тестами (code coverage) в Go?

Привет, коллеги! Я только начал осваивать Go, и у меня возникла проблема с измерением покрытия кода тестами. Пишу небольшой REST API на net/http, и хочу убедиться, что мои тесты покрывают все основные сценарии. Вот пример моего кода: ```go package main import ( "net/http" "testing" ) func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, World!")) } func TestHandler(t *testing.T) { req, _ := http.NewRequest("GET", "/", nil) rr := httptest.NewRecorder() handler(rr, req) if rr.Code != http.StatusOK { t.Errorf("expected 200, got %d", rr.Code) } } ``` Я запускаю тесты через `go test`, но как узнать, какой процент кода покрыт тестами? В документации написано про флаг `-cover`, но я не понимаю, как его правильно использовать. Видел ещё какие-то профили покрытия, но они кажутся сложными. Подскажите, пожалуйста, как новичку: 1. Просто посмотреть общий процент покрытия. 2. Увидеть, какие именно строки кода не покрыты тестами. 3. Сгенерировать HTML-отчёт для наглядности. Буду благодарен за примеры команд и пояснения! Заранее спасибо. <<>>

Основы измерения покрытия в Go

Go предоставляет встроенную поддержку измерения покрытия кода тестами без установки сторонних инструментов. Всё, что вам нужно — это стандартная утилита go test с соответствующими флагами.

1. Простой процент покрытия

Самый простой способ — запустить тесты с флагом -cover:

go test -cover

Вывод будет выглядеть примерно так:

ok      your-module     0.002s  coverage: 75.0% of statements

Если хотите видеть покрытие для каждого пакета отдельно, используйте -coverprofile для сохранения отчёта:

go test -coverprofile=coverage.out
go tool cover -func=coverage.out

Вторая команда покажет таблицу с покрытием каждой функции:

your-module/main.go:8:    handler          100.0%
your-module/main.go:12:   TestHandler      100.0%
total:                    (statements)     100.0%

2. Просмотр непокрытых строк

Чтобы увидеть, какие конкретно строки кода не покрыты тестами, сгенерируйте HTML-отчёт:

go test -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html

Откройте файл coverage.html в браузере. Зелёным цветом будут отмечены покрытые строки, красным — непокрытые. Серым — строки, которые не участвуют в анализе (например, объявления пакетов).

3. Практический пример с вашим кодом

Предположим, у вас есть файл main.go с обработчиком и тест в main_test.go:

// main.go
package main

import "net/http"

func handler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
        w.Write([]byte("Hello, World!"))
    } else {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
}

// main_test.go
package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestHandler_GET(t *testing.T) {
    req := httptest.NewRequest("GET", "/", nil)
    rr := httptest.NewRecorder()
    handler(rr, req)

    if rr.Code != http.StatusOK {
        t.Errorf("expected 200, got %d", rr.Code)
    }
}

Запустим с отчётом:

go test -coverprofile=coverage.out
go tool cover -html=coverage.out

В HTML-отчёте вы увидите, что ветка else (обработка POST/PUT и т.д.) подсвечена красным — она не покрыта тестом. Это подскажет вам написать второй тест:

func TestHandler_POST(t *testing.T) {
    req := httptest.NewRequest("POST", "/", nil)
    rr := httptest.NewRecorder()
    handler(rr, req)

    if rr.Code != http.StatusMethodNotAllowed {
        t.Errorf("expected 405, got %d", rr.Code)
    }
}

Важные нюансы

  • Покрытие считается только для выполняемых строк. Объявления типов, констант и импортов не учитываются.
  • Флаг -covermode определяет режим: set (по умолчанию, просто проверяет, выполнялась ли строка), count (сколько раз выполнялась), atomic (для параллельных тестов).
  • Для больших проектов используйте go test ./... чтобы прогнать тесты во всех пакетах рекурсивно.
  • Не гонитесь за 100% покрытием. Хороший ориентир — 70-80% для бизнес-логики. Главное — покрыть критические пути и граничные случаи.

Теперь вы можете легко оценивать качество тестирования и находить "слепые зоны" в коде!

<<>>

Альтернативный подход: использование сторонних инструментов

Стандартный инструмент go tool cover отлично справляется с базовыми задачами, но для более продвинутого анализа можно использовать gocover или go-test-coverage.

Пример с gocover (установка: go install github.com/axw/gocov/gocov@latest):

# Запуск с профилем
go test -coverprofile=coverage.out

# Анализ через gocov
gocov convert coverage.out | gocov report

Этот инструмент показывает покрытие в более читаемом текстовом формате с разбивкой по пакетам и функциям.

Для CI/CD (например, GitLab CI или GitHub Actions) удобно использовать go-test-coverage:

go install github.com/vladopajic/go-test-coverage@latest
go-test-coverage --config=./.testcoverage.yml

Он позволяет задать порог покрытия и выдавать ошибку, если порог не достигнут. Пример конфига:

profile: coverage.out
threshold:
  file: 70
  package: 80
  total: 85

Если вам нужно визуализировать покрытие прямо в редакторе, установите расширение для VS Code Go Cover — оно подсвечивает непокрытые строки прямо в коде.

Помните: любой инструмент — лишь помощник. Главное — писать осмысленные тесты, а не гнаться за цифрами.