Юнит-тесты
15 лет назад никто не слышал про юнит-тесты. Код писался один раз, потом проверялся вручную QA и методами пристального вглядывания. Все жили в гармонии и мире. Но тут пришли юнит-тесты и мир разделился на два враждующих лагеря: на тех, кто пишет тесты и тех, кто нет.
Спойлер: я за авто-тесты.
В интернете очень много аргументов как за, так и против. Я решил собрать свой собственный список аргументов за юнит-тесты.
1
Тесты пишутся не для того, чтобы протестировать каждое логическое условие и всевозможные комбинации входных и выходных данных.
@Test
public void testSuccess() throws Exception {
ObservationRequest req = createRequest();
int times = (int) ((req.getEndTimeMillis() - req.getStartTimeMillis()) / 1000);
config.setProperty("rotator.enabled", true);
service = new RotatorService(config, predict, new ScheduleFixedTimesTheadPoolFactory(times), new SteppingClock(req.getStartTimeMillis(), 1000));
service.start();
assertNotNull(service.schedule(req, req.getStartTimeMillis()));
try (BufferedReader r = new BufferedReader(new InputStreamReader(RotatorService.class.getClassLoader().getResourceAsStream("expected/rotctrld-requests.txt"), StandardCharsets.UTF_8))) {
String curLine = null;
int i = 0;
while ((curLine = r.readLine()) != null) {
assertPosition(curLine, requestHandler.getRequests().get(i));
i++;
}
assertEquals(i, requestHandler.getRequests().size());
}
}
Я очень часто слышу следующий диалог:
- А вот тут давайте мы напишем юнит-тесты.
- Но мы же не сможем ими покрыть А, Б, В!
- Точно, это будет слишком сложно и нам понадобится сложная инфраструктура. Давайте вернёмся к ним в следующий раз, когда у нас будет больше времени.
Я крайне не согласен с таким подходом. Тесты не должны покрывать всевозможные случаи, А, Б и В. Основное их назначение - хотя бы раз выполнить написанный код. Сколько раз я видел случаи, когда разработчик написал код и ни разу его не выполнил. Наличие теста - это отличное доказательство, что код хотя бы раз выполнился, и совсем глупых ошибок там нет.
2
Даже маленькие библиотеки нужно тестировать. Особенно маленькие библиотеки. Обычно они имеют чётко обозначенный интерфейс, который можно удобно протестировать юнит-тестами.
3
Написание тестов заставляет задуматься о том, как структурирован код. Обычно, разработчик сфокусирован на том, чтобы код работал как нужно. Написание же тестов, позволяет понять то, как код будет использоваться. Я прохладно отношусь к TDD. Но в этой методологии есть зерно правды - надо писать код и тесты в одно и то же время. Это позволяет сильно сэкономить на интеграции кода с другими компонентами.
Ещё при написании тестов начинаешь задумываться: “А что ещё можно проверить?”, “Ничего я не забыл?”. Это позволяет посмотреть на код с другой стороны и придумать пару странных сценариев использования, которые лучше протестировать. Сюда же входят всевозможные проверки на входные параметры и граничные условия.
4
Тесты позволяют править ошибки реализации. Допустим все тесты проходят успешно, но выясняется, что код всё-таки работает неправильно. В таком случае его реализация меняется, и тесты запускаются ещё раз. Ошибок быть не должно.
Этот пункт можно расширить: тесты позволяют обновлять зависимые библиотеки. Допустим, такая ситуация: код зависит от сторонней библиотеки. В этой библиотеке была найдена ошибка безопасности. Если есть тесты, то можно просто обновить версию зависимой библиотеки и прогнать тесты. Ошибок быть не должно. Это очень удобно использовать в связке с автоматическими системами генерации pull request. Например, github недавно научился сканировать зависимые библиотеки на разные уязвимости и автоматически создавать pull request с обновлёнными версиями библиотек. Если есть тесты, то такие pull request автоматически собираются и тестируются. Если нет ошибок, то можно безопасно обновляться.
Выводы
Пишите тесты. А если не знаете как, то спросите меня. Я знаю.