Перевірка покриття коду тестами в Go

В цій статті я вас познайомлю з одною потужною тулзою для аналізу покриття коду тестами go test --cover. Для тих хто займається програмуванням вже певний час слово cover є цілком зрозумілим, для інших дам його переклад – покрити.

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

package math

// Max is a function that returns bigger of two numbers.
func Max(a, b int) int {
  if a > b {
    return a
  }
  return b
}

Думаю пояснювати, що робить функція не потрібно. Тепер маючи функцію, давайте напишимо тест, щоб перевірити її коректність:

package math

import (
  "testing"
)

func TestMax(t *testing.T) {
  res := Max(5,2)
  if res != 5 {
    t.Errorf("Max(5, 2)=%v; want 5", res)
  }
}

Перевіряємо чи тести проходять за допомогою go test. А тепер скористаємося командою яку я навів на початку статті go test --cover. Якщо ваш код відповідає вище написаному, то ви повинні побачити наступний результат:

PASS
coverage: 66.7% of statements
ok      blog    0.007s

Найцікавіше знаходиться в другій лінійці – процент покриття коду тестами. В нашому випадку дві лінійки коду з 3-ох покрито тестами. Давайте уважніше глянемо на початковий код. На самій горі в нас знаходиться назва поточного пакету, цілком очевидно, що тут тестувати немає що. Наступна лінійка пуста, а за нею коментар до функції. Я собі слабо уявляю, як можна протестувати коментар (хіба що граматику і орфографію перевірити), тому цю стрічку теж не розглядаємо. Наступне це визначення функції, таку річ теж не зрозуміло як можна протестувати, тому переходимо в тіло функції. Спочатку в нас йде умова, а після цього дві дії: перша виконується коли умова правдива, а друга коли ні. Також в нас в коді є дві фігурні дужки, але їх теж немає сенсу тестувати (хоча їх правильне розставлення, часом буває критичним). Отже ми знайшли наші три лінійки коду, які було перевірені на покриття. Тепер ж глянемо на тест. До функції Max ми передаємо два числа 5 і 2, оскільки перше з них є більше то в нас виконається return a. Давайте трошки змінимо наш тест (наводжу тільки функцію):

func TestMax(t *testing.T) {
  res := Max(5,2)
  if res != 5 {
    t.Errorf("Max(5, 2)=%v; want 5", res)
  }
  res = Max(2,5)
  if res != 5 {
    t.Errorf("Max(2, 5)=%v; want 5", res)
  }
}

Запустимо ще раз перевірку go test –cover, і отримаємо покриття в 100%. Слід зауважити, що загалом ця утиліта є доволі примітивною, тому 100% покриття зовсім не означає, що ваш код перевірений на всі 100% і що в ньому немає помилок. Як приклад перепишемо нашу тестову функцію наступним чином:

func TestMax(t *testing.T) {
  res := Max(5,2)
  if res != 5 {
    t.Errorf("Max(5, 2)=%v; want 5", res)
  }
  Max(2,5)
}

Покриття тестами і надалі 100%, хоча й останній результат нами повністю ігнорується. Тобто ми можемо у функції Max, замість return b написати return 0і це ніяким чином не повпливає ні на правильність тесту, ні на покриття.

Але для чого потрібна ця метрика? З власного досвіду можу сказати, що є дуже сильна кореляція між якістю коду і процентом покриття тестами. Звичайно ця кореляція не завжди правдива, я бачив код без тестів, який працював роками, а також код зі 100% покриттям з великими логічними дірами. Але як правило високе покриття вказує на кілька фактів:

  1. Щоб програма не робила, її правильність була перевірена за допомогою тестів. Тому вона як мінімум відповідає їхнім вимогам.
  2. Є великий шанс, що під час супроводження і додаванню нової функціональності, попередня логіка не була випадково поломана.
  3. Програма запроектована з розумом. З досвіду скажу, що досягти високого покриття не так вже легко. Дуже часто необхідно переписувати структури та додавати інтерфейси, щоб була можливість використати фальшиві залежності у ваших тестах. А це здебільшого позитино впливає на програму.

Чи варто намагатися досягти 100% покриття? Вважаю, що ні. Якщо пакет або функція дозволяє легко покрити всі лінійки коду тестами, то я це роблю. Якщо якась частина коду не є критичною (наприклад внутрішні метрики) і для її тестування необхідно потратити дуже багато часу на переписування коду і добавленню лишніх абстракцій, то я немаю нічого проти, щоб пропустити тестування в даному випадку. Загалом я б радив тримати рівень покриття близько 80-90% для вашого проекту. Якщо рівень покриття падає нижче 65%, це є хорошим сигналом, що команді необхідно зупинитися і сфокусуватися на тестах, в іншому випадку чекай біди найближчим часом.

Наступного разу спробуємо розглянути як запускати аналіз на більш складних проектах і як аналізувати окремі функції та файли. А тим часом рекомендую почитати статтю про аналіз покриття на офіційному блозі Go.

about author

admin

lobur.marian@gmail.com

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *