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

топ 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, которое будет удобно и полезно длительное время. Сделать такое с первого раза как правило нельзя, нужен некоторый опыт и знание предметной области.

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

Что дальше?

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


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

Архив записей в блогах:
Добрый вечер! У меня опять все сразу, но нам было очень вкусно, может быть что-то пригодится и вам:) БУЛГУР С СУШЕНЫМИ СЛИВАМИ В АРОМАТНОМ МАСЛЕ 2 порции 1/2 чашки крупного булгура 1 и 1/2 чашки кипятка 1/2 маленькой луковицы 1 ч. л. растительного ...
По вопросам размещения рекламы в блоге и паблике ВК обращайтесь: [email protected] Сегодня утром я кушал суп с фрикадельками, орешек со сгущёнкой, вафлю, конфетку. Пошёл платить за квартиры, сначала в банк зашёл. Там было много народу, в первый раз такое, значит, местные жители ...
Я "точно" помнил, как М.Горбачев в своих воспоминаниях писал, что сад его деда вырубили после введения налога на плодовые деревья после войны. Однако, теперь я не нашел в его воспоминаниях подобного. Мало того, что, оказывается, и до войны в СССР был налог на плодовые деревья, но и сам ...
Приезжали очередные дети, дочка и внучка. В пятницу вечером приехали и - ура! - был у нас целый субботний день. Заранее были куплены билеты на сырный завод в Углич, экскурсия начиналась в два часа, и мы поехали в город, несмотря на жару. Я люблю, когда наше производство хорошо ...
Россия отказывается от западного продовольствия. Вряд ли эта новость, прошу прощения за плоский каламбур, для кого-то стала новостью. «Президент России Владимир Путин, накануне поручивший правительству РФ разработать ответ на санкции западных стран, уже сегодня подписал указ об ответны ...