Руководство по написанию тестов
Всем привет, будущие senior java developers! Давно мы с вами не виделись. Так уж сложилось, что работа поглощает большинство моего времени и я не могу так активно контрибьютить в написание статей как раньше, хотя очень хочу этого!
В рамках этой статьи хочу написать свои мысли о том, как стоит писать тесты разработчику, о чем нужно помнить. Казалось бы тема уже должна быть избита, вместе с тем в этом году я провел много собеседований и понял, что соискатели очень плавают в этом вопросе.
Также, для меня важно получить от вас фидбек, чтобы я смог дополнить и выпустить общий гайдлайн.
Заранее спасибо вам)
Ну, не буду дальше тянуть, поехали!
Именование
В тестах для разработки есть два вида тестов - интеграционных и модульные. Как их именовать?
Классы
Возьмем к примеру класс `PluggableAppAdministrationApi`, для него могут быть как интеграционные, так и модульные тесты.
Для интеграционного теста к названию класса нам нужно добавить суффикс `IT`, для модульного просто `Test`:
- `PluggableAppAdministrationApi` -> `PluggableAppAdministrationApiIT` - интеграционный тест
- `PluggableAppAdministrationApi` -> `PluggableAppAdministrationApiTest`- модульный тест
Методы
Название теста должно быть понятным и отражать его суть.
Используйте название метода, который тестируется, в сочетании с ожидаемым результатом или поведением.
Например: `shouldReturnTrueWhenInputIsValid`.
Структура (предлагаемая) для написания тестов
В рамках структуры могут быть разные подходы и описанная здесь не должна быть догматична для всех. Тем не менее, предлагаемый подход ниже
поможет сделать тесты более понятными и читаемые.
Структура тестов через BDD (Behavior Driven Development) позволяет разработчикам, тестировщикам и другим заинтересованным сторонам легко
понимать требования и сценарии поведения системы. Вот основные компоненты, которые помогут разработчикам правильно писать тесты в формате BDD:
- Сценарий - это название теста, каждому сценарию должно быть дано четкое название, описывающее его цель.
- Given (Дано): Устанавливает предысторию и состояние системы. Определяет, из какого состояния начинается тест.
- When (Когда): Описывает действие или событие, которое инициирует поведение системы.
- Then (То): Определяет, какое поведение ожидается от системы после выполнения действия. Это описание ожидаемого результата.
- And (И): Используется для добавления дополнительных условий или шагов для более сложных сценариев.
Пример такого теста из одной библиотеки:
@DisplayName("The most important test. Shown, that the changes in algorithm, "
+ "don't break the main behaviour and result as expected")
@Test
public void shouldProperlyExecuteComparing() {
//given
BufferedImage expectedResultImage = readImageFromResources("result.png");
File file = new File("build/test-images/result.png");
//when
ImageComparison imageComparison = new ImageComparison("expected.png", "actual.png");
ImageComparisonResult imageComparisonResult = imageComparison.compareImages().writeResultTo(file);
//then
assertNotNull(imageComparison.getActual());
assertNotNull(imageComparison.getExpected());
assertEquals(MISMATCH, imageComparisonResult.getImageComparisonState());
assertImagesEqual(expectedResultImage, imageComparisonResult.getResult());
}
Структура BDD позволяет создавать тесты, которые не только проверяют корректность работы системы, но и служат документацией к требованиям.
Такой подход способствует лучшему пониманию между техническими и нетехническими участниками проекта,
а также улучшает качество самого программного обеспечения.
Расположение
Тесты должны располагаться в отдельной папке, обычно это `src/test/java` в проектах Maven или Gradle.
Структура директорий должна дублировать структуру основного кода, чтобы было легко найти тесты, соответствующие классу.
Советы как делать
Принцип по написанию тестов FIRST
Принципы написания тестов, известные под акронимом FIRST, помогают разработчикам создавать качественные и эффективные тесты. FIRST расшифровывается как:
Fast (Быстрые)
Тесты должны выполняться быстро, чтобы их можно было запускать часто и без значительных затрат времени.
Это позволяет разработчикам получать обратную связь об изменениях кода как можно быстрее.
Если тесты будут идти долго, то время деливери продукта будет сильно сдвинуто.
Isolated (Изолированные)
Каждый тест должен быть независимым от других. Это значит, что результаты одного теста не должны зависеть от результатов другого. Изолированные
тесты проще отлаживать и поддерживать, так как проблемы можно точно локализовать. Другими словами, связанный тест - это неправильно
написанный тест, который создаст намного больше хлопот, чем пользы, для которой разработчик решит связать тесты.
Repeatable (Повторяемые)
Тесты должны давать одинаковые результаты при каждом запуске при одинаковых условиях.
Это значит, что тесты нужно писать так, чтобы они были предсказуемыми и не зависели от состояния системы или внешних факторов.
Игнорирование этого принципа ведет к созданию плавающих тестов, которые будут ломать сборку от случая к случаю.
Хороший пример такого плохого теста - это неправильная работа с часовыми поясами может привести к проблемам при запуске в разнах часовых зонах.
Self-Validating (Автоматически проверяемые)
Тесты должны иметь четкие результаты: либо проходят, либо нет. Это упрощает процесс проверки,
так как разработчики не должны вникать в результаты тестов — они просто видят, прошел ли тест или нет.
Timely (Своевременные)
Тесты должны писаться как можно раньше в процессе разработки, желательно сразу после написания кода или даже до него(в практике тестируемого
разработки, например, TDD — тестирование перед разработкой). Это помогает предотвратить ошибки и обеспечивает более высокое качество кода.
Соблюдение этих принципов помогает создавать эффективные тесты, которые способствуют улучшению качества программного обеспечения и
упрощают процесс разработки.
А также
- Пишите короткие и понятные тесты, проверяющие одну вещь. Это повысит их читаемость и простоту в отладке.
- Используйте методы для общих проверок (assertEquals, assertTrue и т. д.) из библиотеки утверждений.
- Не забывайте покрывать крайние случаи и исключения.
- Используйте моки для интеграции с зависимыми компонентами (например, Mockito).
Вместо вывода
Написание тестов это такая неотъемлемая часть разработки, как и написание бизнес логики. Стоимость поиска дефекта на этапе разработки
через тесты, написанные разработчиком на порядок дешевле от стоимости решения дефекта, который нашли в продакшене.
Друзья, предлагаю вам подписаться на мой телеграм канал romankh3
Всем добра и мирного неба над головой!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ