Как стать автором
Обновить

Комментарии 30

Надо понимать, что Unit-тесты — один из способов обеспечить качество. Где-то он хорош, где-то — слишком дорог или неэфективен. Те же Microsoft и Oracle — они же делают всякие SQL Server-ы, компиляторы и рантаймы .NET и Java, там требуется высочайшее качество. Там вообще непонятно как это можно вручную проверять.

Но это не значит, что юнит-тесты везде отобьются. Большинству средней руки приложений, тесты нафиг не надо. Скажем, какой-нибудь UI на реакте, или там какой-нибудь server-side .NET — который из SQL в JSON перекладывает — это всё зачем тестами покрывать? Вот, скажем, хабрахабр тот же — какой смысл по TDD писать?

Да вообще никакого. Вот поломаете кусок сайта, полежит он денек-второй, ничего же не поменяется, правильно?


А потом рано или поздно получается так, что количество поломок при изменениях растет, и вот уже тим. лид/менеджер/бизнес запрещает вам трогать что угодно, что может как-то задеть другой функционал и вы начинаете строить сложные костыли вокруг. Количество костылей растет, они уже не спасают и потом становится все плохо.


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

Не поломаю. У меня есть QA, которые все внимательно проверяют перед релизом. Все равно им проверять всё то, что тесты не ловят: аккуратность верстки, адекватность и понятность UX-а, правописание.

Вообще, сейчас же весь веб пишется как в последний раз — чик-чик, и зарелизили. Поперло — будут деньги переписать, не поперло — хорошо что лишнего не потратили. Там вообще нечего «фиксировать» тестами — всё по-кругу переписывается с нуля.

Я вот почему-то уверен, что в коде того же Хабрахабра тесты если и есть, то где-то в самых ключевых местах только. И фичи они на нас через A/B тестирование проверяют. И ничего, норм всё едет.
Если постоянно все по кругу полностью переписывается и живет в романтическом духе стартапа, то вы правы, тесты тут ни к чему. Они только будут тормозить развитие. Автоматизированные тесты нужны тогда, когда вы что-то фиксируете на длительный срок. В этом случае ваш QA скажет вам спасибо, ведь, как написано в статье, самые простые ошибки программист с большой вероятностью устранит сам, и тогда на поиск сложных у QA останется больше времени.

У меня есть отличная картинка для вас.


Ну и в самой статье отлично написано — настоящий QA не дешевле автотестов, как минимум потому, что люди отвратительно масштабируются. А адекватность верстки и правописание отлично проверяется автоматическими тестами.


А касательно "переписать" — недавно на хабре уже была волна постов на эту тему. Вам может очень не повезти и переписывать никто не даст, так что лучше сначала делать нормально.


Ну и касательно Хабра — я не совсем понимаю, почему этот ресурс внезапно служит у всех каким-то эталоном продукта. Ребята не могли тысячи лет прикрутить markdown в посты и комментарии. И как бы "норм, все едет" — это отличная оценка для бизнеса, который не трогает код, а для программиста — такое себе. Вам работать с этим кодом и строить магические костыли вокруг непонятных участков кода, которые никто не может отрефакторить, потому что они непокрыты тестами и критичные.

Сложность системы и необходимость его тестирования — понятия, связанные друг с другом лишь опосредованно. Писать тесты нужно тогда, когда вам необходимо явно убедиться, что ваше ПО работает и не деградирует с течением времени. Тут неважно, простой ли это транслятор из SQL в JSON, или компилятор. Важно, долго ли вам это ПО поддерживать. Если это простой MVP, который нужно как можно быстрее довести до пользователя и потом переписать, автоматические тесты могут лишь сделать хуже бизнесу. И от них в этом случае лучше отказаться.
Вот, скажем, хабрахабр тот же — какой смысл по TDD писать?

TDD полезен для проработки архитектуры в том числе. Ну, а на Хабре не такая тривиальная бизнес-логика, как кажется на первый взгляд.

Я один из тех кто не понимает зачем нужны unit-тесты. И я Очень хочу что бы кто-нибудь мне все-таки объяснил зачем с ними возиться, если вы пишите бизнес-приложение (На Java в моем случае). Статья увы не открыла для меня каких-то сакральных мыслей, вопрос остается для меня открытым. Я честно пытался внедрить TDD при работе, и вот что вышло: Разрабатывается сквозная фича, на слой за слоем пишутся тесты, потом как обычно это бывает уже под завершение фичи озаряет гениальная идея, код меняется, соотвественно нужно сразу переписать и кучу написанных тестов, ок. Проходит время, меняется бизнес-требование, что делаем? Правильно, меняем кусок кода и пару десятков тестов. И так по-кругу! Я до сих пор не вижу где тут польза, учитывая что на разработку с таким подходом у меня уходило примерно на 40% больше времени, а выловлено багов было всего пару, которое отловили бы тестеры скорее всего. Считаю необходимыми только приемочные авто тесты, а юнит-тесты писать только на критичный core-функционал, вокруг которого строится приложение, а не на все подряд.
О балансе между разными видами автоматизированных тестов говорит пирамида тестирования. Модульные тесты — самые дешевые в разработке: они быстрее остальных пишутся и отдельный тест дешевле и реже меняется по сравнению с другими видами тестирования. Поэтому количественно их должно быть больше всего в общем ряду.

Писать модульные тесты (как и любые другие) на все подряд (чтобы code coverage был почти 100%) — неверно. Я бы даже назвал это антипаттерном поведения. Вы правильно указали, что зачастую бывает достаточно закрыть модульными тестами только фундамент приложения, автоматизировать приемочные тесты, а все остальное отдать на откуп ручникам. На мой взгляд, это позволяет соблюдать баланс между качеством тестирования и скоростью реакции на изменчивые требования рынка во многих проектах.
Обычно если у вас появляется такая проблема, то ваш код не был спроектирован как тестируемый. Можно ли решить эту проблему в рамках вашего проекта — это довольно сложный вопрос. Тот же подход Active Record, на мой взгляд, откровенно плохо покрывается юнит-тестами.
Если вы до сих пор, извините, «дротите» на тесты, но при этом не разрабатываете продукты по мощности уровня компилятора или управления реактором, вам нечего делать в ИТ — вы просто дилетант, прыгающий с хайпа на хайп и убеждающий (прежде всего себя) в нужности этого хайпа. Взрослейте!

А те, кто всё ещё не понял принципа применения тестов, должны наконец уяснить:
Есть СТОИМОСТЬ теста, его ВРЕМЯ ЖИЗНИ, его ПРИМЕНИМОСТЬ и наконец, стоимость ошибки. Если ничего из этого не учитывалось, ваши тесты НЕ НУЖНЫ.

Обычно так оправдываются когда тестов нет

Обычно оправдания ожидает вчерашняя студота, возомнившая себя сеньорами, а у меня своё профессиональное мнение. Хочешь — слушай, не хочешь — продолжай проси*рать время.
Написание тестов должно поддерживаться со стороны менеджмента.… Менеджмент должен поддерживать написание тестов и понимать, зачем они вообще появляются в проекте.


Менеджмента ИТ или бизнеса?
И тем, и другим. Менеджменту бизнеса важен time to market, менеджменту IT — предсказуемость поддержки и сроков. Тесты — один из кирпичиков в фундаменте обеспечения этих потребностей.
Один маленький тест для проверки входных данных — и 2,6 млрд рублей были бы спасены.


А вы думаете, входные данные живут сами по себе? :) Увы, нет. Они пошли в математику и вызвали цепочку неких управляющих воздействий от программы в целом. Проверить такое — та ещё задача.
Вот вам будущая программа одного из устройств. Она на Си и должна работать на микроконтроллере (всего контроллеров 5 и они объединены ДОЗУ), но с обвязкой на Си++ (чтобы можно было отладить на PC — запускается в ОС QNX 6.3). Как бы вы написали бы тесты для такой программы? Я вот даже в принципе этого не представляю.
Можно было написать тест, что поставщик данных, который отдает их в блок математики, работает правильно. Одного теста на это достаточно. То, что математика работает верно, проверяется другими тестами.

В коде, который вы прислали, нет ничего специфичного. Есть интерфейс классов или функции модулей, на них можно написать модульные тесты. Или написать интеграционные тесты на их взаимодействие. Вопрос, как и какими инструментами писать тесты, выходит за рамки этой статьи.
Можно было написать тест, что поставщик данных, который отдает их в блок математики, работает правильно.


В том-то и дело, что нельзя. Диапазон всех этих величин — вся сетка координат. На эти данные программа может реагировать совершенно по-разному.

Есть интерфейс классов или функции модулей, на них можно написать модульные тесты. Или написать интеграционные тесты на их взаимодействие.


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

Вопрос, как и какими инструментами писать тесты, выходит за рамки этой статьи.


Этим грешат все статьи про тесты. Либо в примерах тестируют простую функцию, либо общие слова о полезности тестов. Ни разу я не встретил статьи, где бы тестировалось реальное, относительно крупное приложение на Си/Си++ по полочкам. А вот такая статья очень-очень нужна.
В том-то и дело, что нельзя. Диапазон всех этих величин — вся сетка координат. На эти данные программа может реагировать совершенно по-разному.

Для борьбы с диапазоном значений существует понятие классов эквивалентности тестов. Для обеспечения полноты покрытия можно использовать pairwise testing. С точки зрения программной реализации также будет полезным property-based testing. Для многих языков, в том числе и для C++, существуют готовые инструменты.

Это чисто формальный подход

Если тестировать по методу «черного» ящика, то неважно, как устроена внутри программа. Есть набор входных данных, есть набор ожидаемых выходных. Этого достаточно. Можно тестировать и по методу «белого» ящика. Оба подхода не отрицают друга друга, а лишь дополняют.

Все эти случаи я даже перечислить не могу

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

Ни разу я не встретил статьи, где бы тестировалось реальное, относительно крупное приложение на Си/Си++ по полочкам. А вот такая статья очень-очень нужна

За C++ не ручаюсь, но про то, как мы пишем тесты для iOS-приложения (Objective-C + Swift), расскажем в одной из будущих статей.
Для борьбы с диапазоном значений


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

Есть набор входных данных, есть набор ожидаемых выходных.


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

Без их фиксации разрабатывать тестовую модель бессмысленно. Если вы не знаете, что тестировать, то и тестировать нечего.


Нет, требования к системе как раз есть — она должна обеспечивать живучесть комплекса. Нет множества тестовых последовательностей (их число как в шахматах число ходов — комбинации что после чего отказало) и неизвестен порядок действий программы (он крайне сильно зависит от входных последовательностей и моментов времени, когда они происходят). Как так вышло? Программа ориентируется на здравый смысл, примерно, как человек. Вот эти «здравые смыслы» в неё и заложены. Вопрос только в том, как проверить, что они во всех ситуациях действительно здравые и не забыли ли мы что-то ещё добавить в здравый смысл, что в 0.01% ситуаций поставит программу на неверную алгоритмику исправления ситуации.
Вы описываете систему как полноценный ИИ. Но по факту ведь это не так. У каждой системы есть критерии приемки (даже у Face ID надежность — 1 к 1 000 000, но Apple считает это допустимым для выпуска продукта на рынок). Их можно автоматизировать. Что-то — через модульные тесты, что-то — через интеграционные и т.д.

Да, иногда для полноценного тестирования системы нужно количество тестовых данных, которое вызывает комбинаторный взрыв. Так может быть. Тогда оставляют только крайние значения и что-то из середины, чтобы проверить соответствие критериям надежности. Но вот удостовериться, что для старта ракеты указаны правильные координаты стартовой площадки, не так уж и сложно. Тут одной проверки достаточно.
Нет, это далеко не ИИ. Это некое подобие человеческого подхода для решения очень узкой проблемы рядом простых способов. Вот только эти способы комбинируются множеством вариантов в процессе решения программой задачи.

Для старта ракеты координаты нет нужды проверять — их в программе не хранят и передают извне. Программа формально работает с любыми координатами. Но вот нюансы управляющих воздействий могут получаться разными, в зависимости от координат. И не всегда они верные из-за каких-либо особенностей (просто вообще в принципе не учли какой-либо случай — даже в голову не пришло). Это похоже на решение интегралов в матлабе — в ряде случаев численное интегрирование дает существенно неверное значение из-за особенностей интегрируемой функции. И узнать об этом не так просто — чтобы об этом узнать, нужно как минимум такое искать. А в ПО ракеты просто не подумали о выявленной проблеме. И тест тут не поможет — нельзя тестировать то, что даже в голову не пришло.
Почему не поможет? Вот есть входные параметры: координаты, соответственно мы должны покрыть две ветки поведения (по координатам байконура стартуем), по любым другим не стартуем.
Потому что вы о неверном поведении программы с другими координатами узнали уже после того, как ракета ушла не туда. Исключение составляет случай, когда программа делалась так, что она не универсальна — то есть, управление ракетой сводилось не к регулятору, а к тупой последовательности действий (взлёт, 10 км вертикально, 10 км под углом 45 градусов и 1000 км под углом 30 градусов). Но очень сильно вряд ли там сделано именно так. Всё-таки, обычно делают систему управления с обратной связью. А такая система должна работать при любых координатах пусковой.
Впрочем, я вас немного разочарую — мой опыт работы с НПО-разработчиком систем управления для военных ракет показывает, что со специалистами в части ПО там всё очень плохо (как вам контроль правильности массивов данных, сделанный как обычная сумма чисел? Для гарантированной доставки переданных данных их просто повторяют 4 раза и всё. Потом запрашивают устройство. Если устройство эти данные всё-таки не приняло (скажем, оно в неудачный момент перезагружалось), пробовать снова поработать с устройством нам не надо — мы бодро рапортуем об отказе комплекса. В одном приборе сделали прямой код для кодирования отрицательных целых чисел. Нахрена?! Переделывать не будут — сложно пересдать представителю заказчика. Протокол обмена придумал сумасшедший — в него всё добавляется по принципу «ой, а так не работает — нужен ещё и номер подмассива. А у нас свободных полей (страница максимум 64 байта — MIL STD) уже нет… А мы его засунем вот сюда. Если тут такое число, то это такой массив, а если другое, то это будет номер подмассива. Что вы говорите? То число тоже номером может быть? Хм… Ну и пусть будет.»).
Как осуществляется приемка решения?
Кто и как определяет, что решение удовлетворяет требованиям?
(я сейчас не про автотесты)
Приёмка какого решения? Программного? Очень просто проходит — в инструкцию по контролю вписывают ту ветку, которая основная рабочая плюс пару-тройку веток имитации отказов. Все, конечно, не вписать (как я уже говорил, отказы могут быть в разном порядке), да и одна ветка проходит эдак часов 10-20 — гироскоп требует такого времени на подготовку. Вот и всё. Если всё прошло успешно, изделие сдано.
Если набор требований, по которым будет приниматься решение, заранее известен и конечен, то их (требований) автоматизация и будет являться тестами.
Нет, разве?
Тут следует различать набор по которому принимается решение и набор, покрывающий всё множество входных данных. Поэтому прохождение набора для сдачи сможет не являться полным тестированием программы.
С тестированием плохо, когда требуется отладка реакций на отказы аппаратуры (аппаратура может отказывать множеством способов — от простого обрыва провода и залипания реле, до потери с ней связи). Тут мы тестируем только некоторые базовые вещи.
Вообще, было бы интересно узнать, как тестируют систему управления Space Shuttle…
Я могу понять, что требования у разных заинтересованных лиц могут быть разные. У гос комиссии — одни, у руководителей разработчиков — другие (требования). Но зачем отделять требования от тестов, понять не могу. Тесты — это формализованные требования. Есть формализованное требование, к нему может быть автотест.

Не все требования формализуются, и соответственно тесты к ним неприменимы.

Отдельная история — стоимость автотестов против ручных. Куча кейсов, когда ручные тесты легко переигрывают автотесты.
Потому что при сдаче тестируете вы не отдельное ПО — вы тестируете сложный аппаратно-программный комплекс в целом. ПО тестировать без аппаратуры можно только эмулируя эту аппаратуру (и сделать привязку к этой модели аппаратуры этого ПО), но аппаратура имеет свои особенности и их эмулировать очень-очень непросто (как эмулировать, например, реальный уход гироскопа или глюки конкретного контроллера или помехи в линии, выбивающие CAN нафиг?). Также есть ручные операции («поверните по шкале поворотного стола на 30 градусов», «закройте луч автоколлиматора пластинкой», «отстыкуйте разъём и убедитесь в пропадании информации» и так далее). Сделать на такое автотесты — это проще сразу найти табуретку, верёвку и мыло. В этой сфере нет абстрактного ПО — есть комплекс со своими особенностями и отказами всего, что только возможно. Принципиально разными отказами.

Недавно тут писали, что ПО зачуханого сервера тестируют гораздо больше, чем ПО ракет. Так вот, это не шутка была. ПО ракеты неотделимо от ракеты и тестируется вместе с ней же. Исключение составляет лишь маленькая часть ПО ракеты (скажем, какая-то относительно несложная математика). Её и тестируют более-менее. Всё, что работает с аппаратурой уже протестировать абстрактно не выйдет.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий