100% coverage тести, які нічого не тестують

Володимир Рожков

В далекому 2010 році, коли долар був по 8, я працював на ентерпрайзному проєкті разом з консультантами з компанії Thoughtworks. Тієї, звідки Мартін Фаулер, тієї, що публікує Technology Radar, за яким, ви, ймовірно стежите.

На нашому проєкті не було тестів, тому що в компанії на той час не було культури тестування. Власне, мінусів у такому підході я не бачу, але то вже інша історія.

Консультанти звісно відразу ж прийнялись виправдовувати свій рейт у $3 000 за день роботи та заявили що для успіху проєкту неодмінно потрібно мати високе покриття тестами.

Наш код у 90% випадків виглядав приблизно так:

class Action {
public void doThing(Context context) {
ResultOne resultOne = ServiceOne.getInstance().doThing(context);
ResultTwo resultTwo = ServiceTwo.getInstance().doThing(context, resultOne);
context.setResult(resultTwo);
}
}

Типова імперативно-процедурна ентерпрайзна локшина, де всі та все ходять у бази та зовнішні інтеграції.

Звісно, з коробки такий код не є тестабельним, через статичні методи. Тому спочатку консультанти винесли сервіслукап в окремі методи, а згодом ми зробили там вже нормальний DI.

Але, оскільки більшість коду ходила кудись назовні, то потрібно було писати моки та стаби (різницю питають у вас на співбесідах), а самі тести зводилися до того що ми перевіряли що мок викликається з необхідним параметром, та виглядали десь отак:

class ActionTest {
@Test
public void testDoThing() {
ServiceOne mockServiceOne = mock(ServiceOne.class);
ServiceTwo mockServiceTwo = mock(ServiceTwo.class);
Action action = new Action(mockServiceOne, mockServiceTwo);
Context context = mock(Context.class);
ResultOne mockResultOne = mock(ResultOne.class);
ResultTwo mockResultTwo = mock(ResultTwo.class);
when(mockServiceOne).doThing(eq(context)).thenReturn(mockResultOne);
when(mockServiceTwo).doThing(eq(context), eq(mockResultOne)).thenReturn(mockResultTwo);

action.doThing(context);
verify(mockServiceOne).doThing(context);
verify(mockResultTwo).doThing(context, mockResultTwo);
verify(context).setResult(mockResultTwo);
}
}

Таких тестів ми писали сотні. Цілі спринти були присвячені ретельному моканню. Каверадж відразу полетів у небеса.

Але, як ми можете здогадатися, ці тести нічого не тестували, а просто викривленим чином дублювали вже написану програму, та радикально ускладнювали рефакторинг.

Згодом, я зустрічав такі «тести» в інших компаніях на інших проєктах.

Формально каверадж є, а по факту ні.

Якщо ви знайшли помилку, будь ласка, виділіть фрагмент тексту та натисніть Ctrl+Enter.

Останні статті

Як зробити біг регулярною звичкою, а не тимчасовим поривом?

Ще жодного разу я не прокидався з бажанням вийти на пробіжку. Завжди знаходяться переконливі причини…

16.07.2025

10 помилок, які роблять розробники при написанні API (і як їх виправити)

Блогер та розробник Марк Анрі розповів про головне помилки, які допускають розробники при створенні API.…

15.07.2025

Великий (несанкціонований) експеримент у Reddit та його результати

Reddit не має вишуканого інтерфейсу. У нього також немає крутих, вражаючих функцій. Його API коштує…

14.07.2025

Проект Vend від Anthropic був справжньою гонкою

Переглядати улюблений серіал програму в сотий раз — це як загорнутися в теплу ковдру з…

11.07.2025

Стартапи, що продають дружбу

Знайти друга ніколи не було легко. Але за останні кілька років це стало значно складніше.…

10.07.2025

Не цікаво або чому я програю в гонитві за AI

Часто думаю, що міг би досягти більшого, якби хотів. Давно вже минули часи бурхливого росту,…

09.07.2025