Юнит-тесты и интеграционные

топ 100 блогов levgem03.10.2022

Пропустив через себя API-first подход с полноценным повсеместным внедрением OpenAPI в компании, стало сильно яснее, что у нас происходит с тестами.

Опытные программисты вообще любят тесты, потому что без них совсем плохо и больно. Что такое автоматический тест? Это симуляционный прогон в моделированных условиях.

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

Программисты довольно условно, но разделяют тесты на юнит-тесты и интеграционные (тут пропущена бездна материала, так что упростим пока до этого деления). Интеграционные проверяют что софтина в комплексе, как черный ящик ведет себя так, как от неё это ожидают.

Интеграционные тесты


Грубо говоря задача интеграционных тестов не допустить:

Юнит-тесты и интеграционные

Но в сложном софте такие тесты начинают долго бегать. Наш набор тестов на ноутбуке давно уже не прогнать (займет больше рабочего дня точно), а у многих других это занимает и того больше. При этом интеграционные тесты реально мало проверяют. Только то, что где-то из точки А в точку Б есть хотя бы одна счастливая дорожка.

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

В каждом шмотке кода, который участвовал в создании результирующей ценности, есть много разных ветвлений, сценариев и т.п. Если по пути выполнения какого-то интеграционного теста было 3 модуля с 10-ю вариантами поведения, то неплохо бы проверить тысячу интеграционных тестов, чтобы убедиться, что все варианты приводят к хорошему результату. Причем несложно догадаться, что на прогоне 1000 тестов, чудовищный объём вычислений будет бесцельно дублироваться.

Итого, интеграционные тесты:

  • позволяют проверить весь софт целиком
  •  очень дорогие в исполнении
  • нестабильные, часто мигают
  • их нужно очень много для полной проверки кода (опустим целую бездну математики о том, что такое полная проверка)

Вопрос: зачем же они вообще такие нужны и какие есть альтернативы? Альтернатива довольно простая: это модульные, юнит-тесты.

Юнит-тесты

Есть довольно простой способ начать писать тесты на модули: берем и проверяем, что функции из какого-то шмотка кода ведут себя так, как мы задумали. Такие тесты пишутся легко, непринужденно и так же бессмысленно. Доходит до проверок, что 2+2 всё ещё равняется 4. Т.е. вокруг модуля создается обвязка, которая проверяет, что он ведет себя согласно ожиданиям.

В чём подвох юнит-тестов? В том, что их быстро и часто удаляют. Обычный интеграционный тест — это контракт, который может жить очень и очень долго. У нас есть те, что живут по 10 лет, потому что проверяют то, что не поменялось за эти 10 лет (какой-нибудь rtmp). А вот юнит тест проверяет только функции модуля. Захотели сделать рефакторинг — тесты в помойку. А ведь за каждым тестом кроется какая-то проверка и какие-то оцифрованные знания, баг репорт или чуйка предыдущего автора.

Получится ли сохранить ньюансы юнит-тестов при рефакторинге кода? Практика показывает, что редко.

Но юнит-тесты могут быть в 1000 раз быстрее, и в примере выше будет прогон не 1000 тестов, а лишь 30: по 10 на каждый модуль из 3-х штук.

Итого, такие тесты часто удаляются, с ними теряются знания и опыт, которые осели в виде кода. Дорого и обидно. Да и зачастую бессмысленно, погуглите «two unit tests, zero integration». Без интеграционных тестов совсем грустно.

Изменение подхода

В чём я у нас смог поменять подход? В том, где же проводить границу модулей. При должном развитии продукта можно начать формулировать и фиксировать контракты различных подсистем.

Т.е. не нужно писать юнит-тесты на модуль rtmp_decoder (ну или очень и очень частично в совсем таких специфичных местах). Нужно обрисовать апи всей подсистемы rtmp, договориться о нём с самими собой и принять, что он не будет меняться очень и очень долго. Т.е. по сути речь идет об интеграционных тестах подсистемы.

Например, в эрланге есть апи вокруг TCP-сокетов: https://www.erlang.org/doc/man/gen_tcp.html   Послать данные, прочитать, принять подключение.

По аналогии с ним в OTP team сделали такое же апи для SSL. Очень удобно и уместно: https://www.erlang.org/doc/man/ssl.html

Соответственно мы делаем такое же апи для RTSP и RTMP протоколов и получаются такие же функции listen, accept, connect, send, recv, но только работающие не с потоком байт, а с потоком сообщений, имеющих четкую и внятно описанную структуру.

Зафиксированность контракта позволяет следующий по цепочке модуль тестировать не трогая предыдущий вообще. Ведь мы зафиксировали контракт, а значит в случае проблем, надо разбираться:

  1. нарушение слева
  2. нарушение справа
  3. неправильно сформулированный контракт

Получаются может не совсем юнит-тесты, но всё же это сильно ниже, чем общие интеграционные.

Самый важный здесь момент: надо научиться и быть уверенным в том, что сможешь спроектировать хорошое, долго живущее API, которое будет удобно и полезно длительное время. Сделать такое с первого раза как правило нельзя, нужен некоторый опыт и знание предметной области.

Мы сейчас встаем на дорогу переделывание тестов на такой манер. Это очень долго и сложно, но нужно.

Что дальше?

Планируем инструмент для описания и валидации таких внутренних контрактов в реальном времени и в тестах.


Оставить комментарий

Архив записей в блогах:
Под каждым из этих узоров скрыта та, которая станет Вашим отражение в фантастическом мире бабочек и, возможно, поможет Вам открыть в себе новую непознанную грань. Выберите узор, нажмите В«УзнатьВ», а затем В«ОпубликоватьВ», чтобы посмотреть ...
Это молодежная сборная России по хоккею, выигравшая чемпионат мира среди юниоров у канадцев со счетом 5:3: (Ролик взят у leonid-martinyk ) По свидетельству водителя автобуса, который привез наших хоккеистов в аэропорт Баффало, они были так ...
 До сих пор не отчеренкованный на Трупачке Лёша Кравецкий (Юлин что-то совсем нюх потерял, его же может затмить новая любовь Гавне) - подгурок сравнительно честный. Один из основных тэгов его уютной жежешечки - "контрманипуляция сознанием". ...
В Ямало-Ненецком автономном округе в сезон #зимниесказки выдали первый чумовой капитал на второго ребёнка. Его получили кочевники из Тазовского района Степан Тэсида и Венера Яр. Семья имеет в своём хозяйстве 300 оленей, супруг работает оленеводом, супруга — кочевой ...
Тут мне Антоха, который mazday909 подогнал "правду". И у меня вопрос - вот я не был в далеком зарубежье ни разу и как-то не чувствую себя обделенным, я реально не понимаю - зачем массово куда-то ездить в далекие перди? Кому надо, тот съездит, не? В том же Волгограде, мои ровесн ...