JavaRush /Курси /Go SELF /go tool cover -func/-html

go tool cover -func/-html — як читати звіт покриття

Go SELF
Рівень 31 , Лекція 2
Відкрита

1. Навіщо потрібен go tool cover: перетворюємо відсотки на карту

Коли ви бачите в консолі щось на кшталт coverage: 42.9% of statements, мозок одразу хоче зробити висновок: «Ну, майже половина — отже, все нормально!». Але це приблизно як оцінювати книжку за вагою: цифра є, а користі мало. go tool cover потрібен, щоб перетворити відсоток на карту: які функції тести не зачепили, які гілки if лишилися червоними, де ви закрили «щасливий шлях», але забули про помилку. Це вже не абстрактна метрика, а справжня навігація.

Профіль покриття, наприклад coverage.out, — це дані. go tool cover — це «переглядач» цих даних. У нього є дві головні «лінзи»: -func — таблиця за функціями, а -html — підсвічування вихідного коду.

Схема роботи така:

flowchart LR
    A["go test -coverprofile=coverage.out"] --> B["coverage.out (профіль)"]
    B --> C["go tool cover -func=coverage.out"]
    B --> D["go tool cover -html=coverage.out"]
    C --> E["Таблиця: файл / функція / відсоток"]
    D --> F["HTML: зелений / червоний / сірий"]

2. Звідки береться coverage.out і навіщо він потрібен

Перш ніж інтерпретувати звіт, важливо зрозуміти, звідки беруться дані. go tool cover не запускає ваші тести сам по собі: він не знає, які вхідні дані ви використовували, які тести запускали і що відбувалося. Він уміє читати файл профілю покриття.

Тому перший крок завжди один: згенерувати coverage.out командою go test із прапорцем -coverprofile. У Go це стандартний шлях: -coverprofile автоматично вмикає збір покриття, тож окремо -cover додавати не потрібно.

Нехай у нас є невеликий навчальний пакет calc — ми використовуємо його як пісочницю для тестів і прикладів:

package calc

// Abs повертає модуль числа.
func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

І тест, який покриває лише «позитивний» сценарій:

package calc

import "testing"

func TestAbs_Positive(t *testing.T) {
	if Abs(5) != 5 {
		t.Fatalf("Abs(5) must be 5")
	}
}

Тепер збираємо профіль покриття — приклад команд, які ви запускаєте в терміналі IDE:

go test -coverprofile=coverage.out

Після цього ви побачите і загальний відсоток, і факт успішного прогону. А головне — з’явиться файл coverage.out, який далі читає go tool cover.

3. go tool cover -func: рентген функцій

Режим -func — це найшвидший спосіб зрозуміти, куди дивитися насамперед. Він виводить покриття за функціями й загальний підсумок. Його зручно сприймати як «список підозрюваних»: де 0%, де 12%, а де раптом 100%.

Важливо, що -func — це зведення: він не пояснює, чому рядок не покрито, і не показує, яка саме гілка if не виконувалася. Зате він дуже економить час на первинній діагностиці.

Команда виглядає так:

go tool cover -func=coverage.out

Вивід буде приблизно таким (цифри у вас можуть відрізнятися, формат — той самий):

abs.go:  Abs    50.0%
total:   (statements) 50.0%

Тут важливо читати рядок як контракт:

  • ліворуч зазвичай ім’я файла (abs.go);
  • далі ім’я функції (Abs);
  • потім відсоток покриття операторів (statements) усередині цієї функції.

А рядок total: (statements) — це загальний підсумок за пакетом або набором профілю.

Чому Abs вийшов 50%? Тому що у функції є дві гілки: x < 0 і «інакше». Ваш тест пройшов лише через «інакше». Гілка return -x лишилася непокритою.

Таблиця для швидкої орієнтації

Що ви бачите в -func Що це означає на практиці
0.0% у функції Ця функція взагалі не виконувалася тестами або виконувалася лише в ділянках, що не покриваються, але на базовому рівні таке трапляється рідко.
100.0% у функції Усі оператори всередині функції виконувалися хоча б один раз. Це не гарантія, що тести хороші.
«Середній» відсоток Часто означає пропущені гілки if, ранні return або перевірки помилок.
total: (statements) Зведення за всім профілем: зручно як «до/після», але не як єдиний орієнтир.

4. go tool cover -html: підсвічування вихідного коду

Коли ви вже знайшли підозрілі функції через -func, хочеться побачити конкретику: які рядки не виконувалися. І саме тут -html генерує HTML-сторінку з підсвічуванням вихідного коду: покрите зазвичай зелене, непокрите — червоне, а іноді трапляються сірі, неінструментовані ділянки.

Саме цей спосіб наочно показує, де вам бракує тестів: у яких гілках, яких return, яких обробках помилок.

Запускається так:

go tool cover -html=coverage.out

Після запуску зазвичай відкривається браузер, або ви відкриваєте файл вручну, і бачите свій код, розфарбований як світлофор.

Як читати HTML-звіт спокійно

Важливий момент: червоне не означає «код поганий». Червоне означає, що в поточному наборі тестів цей код не виконувався. Це може бути:

  • справді забута гілка;
  • гілка, яку неможливо виконати за контрактом, і тоді, можливо, код варто спростити;
  • гілка, яка виконується лише під час помилок введення або I/O, а ви її не тестували.

У прикладі з Abs HTML покаже червоним рядок return -x, і це дуже конкретний сигнал: «Додайте тест на відʼємні числа».

5. Як інтерпретувати покриття по-інженерному: закриваємо ризики

Найчастіша пастка новачка — почати «набивати відсотки» заради відсотків. Це призводить до тестів у стилі «викликав функцію — перевірив, що не впало». Відсоток гарний, а баги тим часом живуть собі спокійно.

Правильна інтерпретація звіту покриття інша: ми використовуємо його як підказку, де в нас сліпі зони, особливо в розгалуженнях і ранніх поверненнях. Покриття не доводить правильність, але чудово показує, що ви навіть не намагалися це перевірити.

Є кілька типових місць, де червоне в HTML майже завжди означає реальну прогалину в тестуванні.

Розгалуження if/switch і «друга половина логіки»

Якщо ви бачите, що в if зелена лише одна гілка, це пряме запрошення: «Протестуйте альтернативу». Іноді альтернатива — негативний сценарій, іноді — граничне значення.

Ранні return і «перевірки помилок»

Дуже часто червоними виявляються гілки виду:

if err != nil {
	return 0, err
}

Це не соромно. Це нормально. Просто це означає, що ви ніколи не моделювали помилку. На рівні нашого курсу ми поки не будуємо складні моки, але навіть у простих функціях можна створювати тестові входи, які призводять до помилки, наприклад поганий рядок для парсингу.

Код «про всяк випадок»

Іноді покриття підсвічує червоним шматок коду, який фактично ніколи не має виконуватися, наприклад заглушку або код, який ще не встигли видалити. У цьому випадку покриття не просить «написати тест», воно просить «ухвалити рішення»: або цей код потрібен, і тоді його треба тестувати, або він не потрібен, і тоді його треба прибирати.

І так, видалення коду — теж вид оптимізації, тільки для мозку.

Частота виконання та режими покриття

Іноді корисно розуміти не лише, виконувалося щось чи ні, а й скільки разів це відбулося. Це особливо помітно, коли ви збираєте профіль у режимі count, а потім відкриваєте HTML: виходить майже «тепловізор» — десь код виконувався багато разів, десь — мало.

На нашому рівні важливо запам’ятати ідею: -func дає зведення, -html дає візуалізацію, а режими покриття впливають на те, які дані закладені в профілі.

Але важливо не плутати покриття з профілюванням продуктивності: «часто виконувалося» не означає «повільно», це лише частота виконання операторів у межах тестів.

6. Приклад: звіт → тест → поліпшення покриття

Зробімо приклад «до/після» на нашому calc.Abs, щоб зв’язок «звіт → дія» був не теоретичним.

Крок 1. Був лише позитивний тест

Код функції:

package calc

func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

Тест був лише такий:

package calc

import "testing"

func TestAbs_Positive(t *testing.T) {
	if Abs(5) != 5 {
		t.Fatalf("Abs(5) must be 5")
	}
}

Збираємо профіль і дивимося зведення:

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

Очікувана логіка звіту: Abs буде покритий частково, тому що гілка x < 0 не виконувалася.

Крок 2. Додаємо тест на відʼємне число

Додамо другий тест:

package calc

import "testing"

func TestAbs_Negative(t *testing.T) {
	if Abs(-7) != 7 {
		t.Fatalf("Abs(-7) must be 7")
	}
}

Тепер знову:

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

Що ви маєте побачити:

  • у -func відсоток у Abs стане 100% або близьким до нього, якщо у файлі є ще щось;
  • у -html обидві гілки стануть зеленими.

І ось це — правильний ритуал: не «гонка за відсотком», а «побачив пропущену гілку → додав тест, який справді проходить через неї».

7. Типові помилки під час інтерпретації go tool cover

Помилка №1: дивитися лише на загальний відсоток total і ігнорувати деталі.
Загальний відсоток зручний, щоб порівнювати «до/після», але він майже марний, коли треба відповісти на запитання «що саме я не протестував?». Правильна звичка: спочатку швидко подивитися -func, щоб знайти слабкі місця, а потім відкрити -html і побачити конкретні червоні рядки.

Помилка №2: намагатися запускати go tool cover, не створивши coverage.out.
go tool cover читає профіль, а не запускає тести. Тому якщо файла немає або він застарілий, ви або отримаєте помилку, або аналізуватимете вчорашній стан. Дисципліна проста: спочатку go test -coverprofile=coverage.out, потім уже go tool cover

Помилка №3: сприймати червоні рядки як сигнал «переписати все».
Червоне означає «не виконувалося в тестах», а не «зламано». Іноді це сигнал додати один тест на граничний випадок — і все. Іноді це сигнал, що гілка взагалі не потрібна, тож її варто видалити.

Помилка №4: домагатися 100% покриття будь-якою ціною і писати беззмістовні тести.
Можна написати тест, який просто викликає функції й нічого не перевіряє, і часто це «підніме відсотки». Але такий тест майже не ловить баги й створює хибне відчуття безпеки. Покриття — це підказка «куди дивитися», а якість тесту — це ваші перевірки (want/got) і зрозумілі сценарії.

Помилка №5: забувати про гілки помилок і ранні повернення.
Часто непокритими залишаються саме перевірки помилок. У підсумку «щасливий шлях» зелений, а будь-який збій перетворює програму на сюрприз. Якщо HTML підсвічує червоним if err != nil { return}, це зазвичай хороший кандидат для окремого тесту, який створює помилковий вхід і перевіряє, що помилка обробляється очікувано.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ