Разделы портала

Онлайн-тренинги

.
Тестируя ваши тесты
12.10.2023 00:00

Автор: Баз Дейкстра (Bas Dijkstra).
Оригинал статьи
Перевод: Ольга Алифанова

В этой статье я хочу обсудить практику тестирования ваших тестов и поговорить о том, зачем это нужно, и как это делается.

Дабы меня правильно поняли – говоря о тестах, я говорю о точках верификации, выполняемой инструментально, то есть о том, что множество имеющих отношение к тестированию людей называют «проверками». Возможно, хорошей идеей будет также тестирование ваших идей, мыслей и относящихся к тестированию процессов, но об этом сегодня речь не пойдет.

Итак, зачем нам тестировать наши тесты? Я считаю это очень хорошей идеей по ряду причин:

  • Тесты – это ПО, а при создании ПО люди делают ошибки. Так как мы хотим снизить связанный с этими ошибками риск, может понадобиться тестировать наши тесты.
  • Чем более автоматизирован процесс создания, тестирования, деплоя и поставки ПО (а в этом направлении движется множество команд), тем выше возложенная на наши тесты ответственность. Все чаще и чаще тесты – единственная страховочная сетка (детектор изменений) между кодом, написанным на машине разработчика, и кодом, оказывающимся на проде. Следовательно, хорошей идеей будет удостовериться, что тесты обнаруживают изменения, которые должны обнаруживать.

Иными словами, мы должны иметь возможность доверять нашим тестам. Однако тесты, как и любое другое ПО, могут нас обмануть. В случае с тестами это обычно происходит при ложноположительных или ложноотрицательных результатах.

Ложноотрицательные очень раздражают – они кричат «Волки», когда в тестируемом ПО нет никаких проблем. Как правило, проблемы кроются во временных привязках, синхронизации, тестовых данных или тестовом окружении.

Но ложноотрицательные результаты хотя бы сообщают о себе, объявляя о падениях. В отличие от них, ложноположительные – это то, что я обычно называю «бесшумными убийцами»: они сообщают об успехе и дают изменению просочиться незамеченным. Вот упрощенный пример кода из воркшопа по мутационному тестированию, который я десятки раз проводил на конференциях и у клиентов:

public void withdraw(double amountToWithdraw) throws WithdrawException {
    if (amountToWithdraw > this.balance && this.type.equals(AccountType.SAVINGS)) {
throw new WithdrawException("Cannot overdraw on a savings account");
}
    // process funds withdrawal here
}

Тест, написанный нами для покрытия этой логики:

@Test
public void overdrawFromSavingsAccount_shouldThrowWithdrawException() {
    Account account = new Account(AccountType.SAVINGS);
account.deposit(30);
    assertThrows(WithdrawException.class, () -> account.withdraw(50));
}

Видите проблему?

Разработчик может ошибиться (они живые люди, поэтому с шансами рано или поздно они это сделают) и случайно упустить вторую часть условного утверждения. Код будет выглядеть так:

public void withdraw(double amountToWithdraw) throws WithdrawException {
    if (amountToWithdraw > this.balance) {
throw new WithdrawException("Cannot overdraw on a savings account");
}
    // process funds withdrawal here
}

Однако наш тест продолжит срабатывать, а покрытие кода останется стопроцентным. Если такой код уйдет на прод, то овердрафт станет невозможным для любых счетов, что потенциально приведет к сбитым с толку пользователям, а также к потере дохода для банка, который не сможет взимать процент за использование заемных средств.

Это всего лишь один пример ценности тестирования своих тестов. Вместо того, чтобы полагаться на срабатывающие тесты и некий процент покрытия (кода), тесты стоит более тщательно… ну, тестировать.

Вот ряд техник, которыми можно пользоваться для тестирования своих тестов:

  • Объединитесь в пару с другим тестировщиком, автоматизатором или разработчиком. Одна голова хорошо, а две лучше, и парная (совместная, групповая) работа – отличный способ быстро получить обратную связь, заметить и исправить потенциальные баги, обсудить подходы к решению проблем, поделиться знаниями и опытом, и т. п.
  • Проводите код-ревью тест-кода. Если ваш контекст не допускает синхронного сотрудничества, как описано в первом пункте, внедрение ревью ваших тестов – еще один способ заполучить вторую (третью, пятую) пару глаз, разглядывающую ваши тесты, что, опять же, сильно повышает шансы найти ненадежные тесты до того, как они подставят вам ножку.
  • Периодически проходите по своему тест-набору и выявляйте тесты, которые утратили надежность и/или ценность. Как и код приложения, код тестов со временем развивается и гниет. Сегодня это эффективный и нужный тест, но в будущем это может измениться. Вместо того, чтобы хранить его просто потому, что вам жаль потраченного времени, обновите или удалите его, пока он не начнет утрачивать пользу. Не попадайтесь в ловушку невозвратных затрат.
  • Используйте техники вроде мутационного тестирования, чтобы получить важную информацию о качестве своих тестов. Инструменты мутационного тестирования могут быть очень ценными помощниками при выявлении пробелов тест-покрытия и уязвимостей имеющихся тестов. Возможно, ими не стоит пользоваться для каждого билда, как минимум из-за длительности запуска, но тщательное, периодически проводящееся мутационное тестирование может серьезно повысить качество вашего тест-набора.