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

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

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

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

Наш код у 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);
    }
}

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

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

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

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

Писали такі тести? Згодні з тим що вони марні чи вважаєте що все правильно нам консультанти сказали? Діліться у коментах👇


Сподобалось? Долучайтеся до мого телеграм каналу: https://t.me/full_of_hatred