Испытания: это измерения
Испытания считаются важнейшим видом свидетельств успешности/качества. Испытания — это измерения с целью получить обоснование претензии. Как и любое измерение, это действие, требующее ресурсов и времени. Даже «восприятие», как отмечалось в руководстве по системному мышлению — это действие, это не пассивная «фильтрация сигналов из окружающего мира»:
- Планирование измерения, оно не происходит «случайно само». Нужен выбор того, что измерять (причинно-следственные связи с целью: инженерное обоснование) и тут важно сгенерировать достаточное число альтернатив, выбор того, как измерять (тоже из достаточного числа альтернатив, с которыми хорошо бы быть знакомым), планирование ресурсов измерения и обоснования, обеспечение их выделения (координационная работа). Это всё требует удержания внимания (в инженерии это означает, что всё это записывается, а не происходит «в уме»).
- Подготовка измерения. Даже если речь идёт о простейшем «внешнем осмотре», это означает, что нужно физически привести во взаимодействие измеряемый объект и измерительный прибор, то есть как-то подтащить измеряемый объект к проводящему наблюдение инспектору или подвести инспектора к измеряемому объекту. Это время и ресурс как наличия и логистики от места изготовления к месту осмотра объекта, так и наличия и логистики от его текущего места к месту осмотра инспектора (без приборов при «внешнем осмотре», или с приборами. Впрочем, сейчас и при внешнем осмотре вполне могут фотографировать, то есть и тут приборы).
- Взаимодействие в ходе измерения. И хотя в случае квантовых измерений шаг по выводу информации измерения за пределы датчика рассматривают отдельно (бит — это результат измерения, и может копироваться; кубит — результат измерения, но не может быть откопирован)[1], будем считать, что речь идёт о классическом в смысле физики измерении с результатом в битах. И этот результат тоже как-то документируется. И если речь идёт о статистических результатах, то этих взаимодействий (и подготовок к ним) в измерениях может быть множество, а сами измерения могут занимать и фемтосекунды, и годы.
- Принятие решений на основе результатов измерения. Если это эксперимент в рамках попперианского понимания науки, то он определяет выбор той теории, которая будет считаться фальсифицированной/опровергнутой (эксперимент как измерение какой-то характеристики при определённых условиях производится для опровержения тех теорий, которые хуже предсказывают результаты измерения)[2], если это инженерное обоснование качества/успешности системы, то результат обоснования: удовлетворение или неудовлетворение претензии/claim.
Есть огромное число разных вариантов проведения испытаний, которые породили обширную терминологию, связанную главным образом с тем, как готовятся измерения и с особенностями проведения измерений (список тут абсолютно неполный, это просто примеры того, что часто встречается в разговорах об испытаниях как лучшем источнике свидетельств для аргументирования претензий на успешность/качество системы):
- Мониторинг — это «постоянно идущее в ходе эксплуатации испытание», входные характеристики окружения при этом задаются эксплуатационной средой, а результаты оцениваются для обоснования заключения об успешности эксплуатации, или о необходимости ремонта («ремонт по состоянию», скажем, акустический датчик на трубе слушает шорох песчинок в протекающей по трубе с какими-то отложениями на стенках нефти, на основе анализа этого шороха делается вывод о работоспособности трубы и необходимости её чистки или даже замены), или о коррекции эксплуатационного режима (цифровой двойник). Если речь идёт о подобном в какой-то определённый момент (например, в момент старта работы), то говорят не о «мониторинге», а самотестировании/самодиагностике. Система мониторинга тут — часть системы. Если это вопросы безопасности и защиты, то вместо «мониторинга безопасности» лучше говорить о «сигнализации» как системе и обсуждать, что там должны делать все остальные в ответ на сработавшую сигнализацию. Скажем, в иммунной системе часть — сигнализация, а часть — средства устранения угрозы, всё это части организма. Очень часто «поставщики систем безопасности» поставляют не «системы безопасности», а только сигнализацию — а уж кто там будет реагировать и что делать в ответ на сигнал о проблеме, это остаётся за рамками проекта. То, что вместо «безопасности» предлагается только «сигнализация в никуда» — это надо обсуждать всегда явно, это типовая ошибка.
- Всевозможные тестируемые объекты («железо»[3], софт, модели, люди, системы) in**-the-**loop, причём loop тут понимается как симулятор надсистем, то есть симулятор окружения, в которое помещается тестируемый объект, на входы-выходы которого подаются нужные для полноценного тестирования сигналы (для «железа»), нужные значения на API (для софта), нужные параметры (для модели, чаще всего это компьютерная модель), нужные стимулы (для людей), нужные изменения в среде (для систем), а потом считывается реакция и происходит её анализ. Этот самый loop иногда называют «стендом». Этот цикл «задать набор характеристик системного окружения, предъявить системное окружение с этим набором характеристик, получить отклик тестируемой системы, задокументировать, вернуться к заданию набора характеристик» происходит в случае «стенда» чаще всего в разы дешевле и быстрее, чем в реальной жизни, где нужно долго ждать, пока встретятся какие-нибудь редкие сочетания входных параметров. Скажем, если у вас hardware-in-the-loop (компьютер какого-нибудь гаджета с его прошивкой), то вы можете подавать на вход самые разные значения, якобы получаемые от датчиков этого гаджета, но на самом деле генерируемые «стендом» и смотреть на отклик так же быстро (не глазами, а считывая значения, выдаваемые компьютером гаджета для актуаторов — это иногда моторчики, иногда дисплеи, иногда что-то ещё). Если бы вы помещали этот гаджет вместе с датчиками в какую-то реальную среду и делали каждый раз реальный замер (например, не подав сигнал датчика температуры «как будто у нас -30°C», а реально прикрепив датчик и охладив его до нужной температуры), то это было бы в разы и разы медленней и дороже.
- Регрессионное тестирование — это повторное тестирование того, что уже вроде как тестировалось. Оно наиболее развито в программном обеспечении, где считается хорошим тоном прогонять все тесты в автоматическом режиме (это относительно дёшево) при любом изменении. Но и для «железа» оно тоже довольно часто. Скажем, вы тестируете тормозной путь, но поменяли шины. И опять тестируете на тормозной путь. Или поменяли бортовой компьютер, а не шины — и всё равно тестируете. Повторный экзамен для людей часто не менее осмыслен: профессиональные ассоциации заставляют переэкзаменовываться каждые несколько лет, это как раз и есть регрессионное тестирование (при этом тесты ЕГЭ являются более-менее бессмысленными, и уже через пару лет после школы их мало кто сможет сдать повторно, поэтому вопрос о регрессионном тестировании «готовности к жизни» не поднимается, ЕГЭ имеет абсолютно другие цели, не связанные с готовностью школьников ко взрослой жизни).
- Юнит-тестирование (тестирование отдельных модулей) и системное тестирование (системы в целом) и приёмочные/acceptance (хотя тут есть терминологическое противоречие с приёмкой/validation) испытания обсуждаются в плане необходимости соблюдения некоторого баланса между ними. Проблема в том, что из-за системного эффекта обоснование успешности в работе отдельных модулей не гарантирует успешности работы системы в целом, и тем более работы надсистемы, да ещё и в необходимом разбросе параметров условий эксплуатации. Но заведомо неуспешный модуль вполне может вести к заведомой неуспешности системы, и его довольно легко обнаружить. Системное же тестирование крайне дорого. Вопрос в том, сколько внимания при фиксированном бюджете на тестирование уделять испытаниям модулей, и сколько испытаниям системы. В программной инженерии TDD, ВDD, ATDD и прочие варианты стиля разработки, ориентированные на test first, по сути сводятся к тому, что сразу пишется «испытательный стенд», как сказали бы в «железной инженерии», который проверяет результаты работы одного модуля/юнита (unit-testing). Попутно выяснилось много интересного: независимо написанные тесты оказались качеством не лучше, чем написанные самими разработчиками модуля. Но два уровня (юниты и система) — это только начало разговора, сегодня это обсуждается как п****ирамида тестирования. Идея в том, что на нижнем уровне пирамиды (юнит-тестирование) используют быстрые простые тесты, на уровне API уже тесты изощрённые, а на уровне UI — сложные и длительные сценарии тестирования. Результаты этого подхода в 2025 году (конечно, этот подход поддерживается современным инструментарием, без которого результаты невозможны)[4]: выполнение тестов на 65% быстрее, покрытие кода тестами на 40% выше, уменьшение числа сбоев в ходе эксплуатации на 90%. Это в программной инженерии, но в «железной» инженерии всё то же самое: проверки делаются быстрые на буквально каждой операции, но есть ещё и испытания на специальных стендах с трендом превратить систему в испытательный стенд для самой себя.
- Автоматизированное тестирование[5] в противовес ручному тестированию. Если понятно, что должна делать программа (но это надо задокументировать! И почему бы не сразу на языке тестов?), то можно поручить какой-нибудь программе составить программу испытаний/тестирования (набор тестов/измерений), а потом «прогнать тесты/замеры». И получить на выходе список результатов тестов/замеров, часть из которых которые будут «неприятным сюрпризом», «провалом испытаний». Проблема в том, что для получения ошибки может быть задействован довольно хитрый и длинный сценарий, и простой случайный перебор сочетаний входных параметров не даст выявленной ошибки, поэтому современные методы стараются анализировать код, чтобы найти ещё не проходившиеся его коды. Если тестировщик думает о том, какую ошибку мог бы совершить разработчик, то он тоже действует не случайным перебором, а целенаправленно — и находит ошибку. Но человек-тестировщик крайне медлен и крайне дорог. Проще добавить вычислительной мощности и поручить тестирование алгоритму. Но чем больше становится система, тем трудней в ней найти ошибку, даже если пытаться как-то делать «метод тыка» менее случайным. Сейчас ручное и автоматизированное тестирование сочетаются, хотя исход этого соревнования заранее ясен: победит автоматизированное тестирование — по мере того, как растёт роль vibe coding и Software 3.0, программы AI тоже будут начинать с test first.
- Инженерияхаосом/chaosengineering[6] как проведение испытаний путём планового отключения каких-то модулей на работающей системе и проверки того, как это влияет на работоспособность системы. Отличие от «аварии» только в том, что это отключение происходит в заранее известный момент времени, и известно, что именно выключается (поэтому есть шанс всё вернуть на место, если что-то пойдёт не так). Это очень жёсткий способ испытаний, в чем-то эквивалентный древним способам испытания мостов, когда под мостом стоит главный инженер и его команда, а по мосту движется первый грузовой поезд, да ещё и с максимальной загрузкой. Представьте, когда вам нужно по плану заглушить двигатель самолёта в полёте, и проверить, что самолёт нормально летит дальше на оставшихся двигателях. Или заклинить один из рулей и проверить, что остальных рулей хватает, чтобы лететь нормально. В случае программных систем это не выглядит так драматично, но представьте, если вам нужно оторвать интернет-соединение, за которым находятся три миллиона ваших клиентов — сколько от них прилетит звонков, если что-то пойдёт не так? Какую ответственность вы почувствуете за их убытки? Но зато при инженерии хаосом вы будете уверены, что при всяких «неожиданностях» ничего особо неожиданного не произойдёт, «у нас отвалилось крыло, как пару месяцев назад на плановом отвале крыла, занимаемся определением причин поломки, но летим нормально, жалоб нет».
- … методов проведения испытаний/тестирования множество, и каждый день появляются новые, список можно продолжать и продолжать. Отслеживайте их, что считалось верхом совершенства вчера — сегодня уже вчерашний день. Стратегируйте: регулярно проверяйте ваш выбор методов проведения испытаний.
Поскольку в программной инженерии можно легко делать множество версий системы в день (изготовление по большому счёту проходит автоматически и мгновенно по сравнению со временем написания исходного кода программ, хотя и тут ситуация меняется: AI-системы пишут код быстро, и с каждой новой версией своих моделей качество этого кода лучше и лучше), то новинки в методах программной инженерии появляются где-то на 10 лет раньше, чем аналогичные новинки появляются в классической «железной» инженерии и инженерии кибер-физических систем. Этот разрыв быстро сокращается (скажем, с появлением таких технологий, как 3D-печать, а также технологий точного физического моделирования), но в целом для понимания фронтира в инженерных методах, включая использование AI, целесообразно смотреть именно на программную инженерию — и знание мета-мета-модели поможет избавиться от особенностей предметной области с её мета-моделью и перенести новые идеи в проектную ситуацию другой предметной области, инженерии систем другой природы.
Для понимания, в какую сторону развиваются методы инженерных обоснований, нужно прежде всего рассмотреть метод DevOps в программной инженерии, где сегодня поддерживается
- непрерывная/continuous сборка/интеграция (CI): в любой момент времени разработки есть работающая версия системы с конфигурацией без известных конфигурационных коллизий,
- непрерывное введение в эксплуатацию (continuous delivery, CD): в любой момент времени есть поставляемая пользователям полезная версия системы, а хоть и с ограниченной функциональностью по сравнению с задуманной, то есть рабочая конфигурация не просто «есть», а ещё и доступна для использования и поддерживается (иногда говорят про continuous deployment, но это просто «развёрнуто на месте использования и настроено-налажено», но доступности для использования и поддерживаемости пока нет, это отдельные действия),
- непрерывная разработка/development: разработка идёт всё время проекта, реализуются всё новые и новые возможности системы, нет момента «мы закончили работать, теперь пользуйтесь, а мы пошли отдыхать» — по факту система эволюционирует во множестве маленьких жизненных циклов, идут умные мутации в разных частях системы, пользователи получают всё новые и новые версии системы в новых и новых вариантах конфигурации,.
- непрерывное обоснование качества/conutinuousQA**,** сводящееся к тому, что обоснование качества ведётся непрерывно в ходе всего проекта[7].
Сама идея DevOps в том, чтобы разработчики брали на себя больше методов из правой части V-диаграммы или ромб-диаграммы жизненного цикла: эксплуатацию (а не сдавали систему каким-то внешним операторам), введение в эксплуатацию (а не оставляли это тем же операторам), а также инженерные обоснования, что их система успешна (а не оставляли это тестировщикам). То, что «осталось справа» — автоматизируется, это первая задача инженеров внутренней платформы разработки.
Без DevOps методов (это до сих пор часто встречается в «железной инженерии») инженеры-разработчики отправляют очередную версию системы тестировщикам («инженерам по качеству», испытателям), а те возвращают список дефектов, инженеры-разработчики опротестовывают эти дефекты («это дефекты в ваших тестах и методике испытаний, а не в нашей системе, и вообще с такими дефектами можно эксплуатировать»), затем после исправления ошибок тестировщики отправляют систему операторам-администраторам, которые её сначала собирают/integrate, затем разворачивают/deploy и настраивают, затем выдают доступ пользователям системы (delivering) — и дальше пользователи обвиняют во вновь найденных ошибках операторов (в подгорелой котлете виноват официант, а не кухня: почему принёс это?), те обвиняют тестировщиков (почему пропустили это?), те обвиняют разработчиков (почему сами не оттестировали это?). DevOps заставляет всех работать с использование общей платформы/инструментария/конвейера/трубопровода/pipeline. В случае ресторана повара берут ответственность на себя за доходящее до клиента блюдо, а не отгораживаются официантами и службой контроля качества. Основной способ, которым такое становится возможным — это автоматизация и тестирования, и разворачивания/«ввода в эксплуатацию»/transfer/deployment, и даже пользовательской поддержки (например, in-line help/«контекстная помощь»). И ещё автоматизируется даже то, что раньше плохо понималось, например, мониторятся fitness functions, что оказывается важным не разработчикам, а архитекторам.
У подхода DevOps в части инженерных обоснований есть альтернативный вариант, который начинался в Гугл, а затем стал весьма распространён. Называется он SRE (Site Reliability Engineering)[8] и одно из важнейших там отличий от «просто DevOps и инженерии платформы» — это различение работ по исправлению найденных ошибок и работ по увеличению функциональности. Подход SRE говорит, что обоснованная успешность системы**—** это прежде всего надёжность, безошибочность функционирования тех её возможностей, которые уже есть. И уже потом**—** добавление новых возможностей. Поэтому новые возможности разрешается добавлять только после того, как уровень известных ошибок в системе снижается до заранее оговорённого. До этого или уже разработанные новые возможности «кладутся в стол» и не вводятся в эксплуатацию, или их вообще прекращают разрабатывать, ибо силы всех доступных разработчиков направляются на устранение найденных ошибок, новая функционально просто перестаёт разрабатываться, все инкременты выпускаются с исправлением ошибок, а не с новыми фичами.
Как в случае осетрины, которая «или первой свежести, или не осетрина», так и в SRE: система признаётся (успешной) системой только тогда, когда на неё можно положиться. Иногда совместно рассматриваемые архитектурные характеристики**, все эти -ости»/-ilities называют dependability, как раз указывая, что «на эту систему можно положиться», и инженерное** обоснование должно это обосновать на базе каких-то свидетельств, а не «умозрительно».
Прогресс в развитии подходов и DevOps, и SRE, включая такие радикальные варианты обоснований как инженерия хаосом, существенно зависит от прогресса в автоматизации. Настолько зависит, что SRE (куда намеренно включается и работоспособность аппаратуры, и учёт физических внешних воздействий типа обрывов оптоволокна интернет-каналов, ураганов с возможным обрывом энергопитания и т.д.) декларирует рассмотрение всех этих проблем как «проблемы программного обеспечения», то есть недостаточной автоматизации в учёте всех неожиданностей и недостаточной автоматизации в разворачивании исправлений для известных ошибок в программах и аппаратуре.
В программной и «железной» инженерии, включая совместную для них инженерию кибер-физических систем, непрерывное инженерное обоснование (continuousQA**)** в рамках DevOps****с инженерией платформы и SRE**—** это сегодня основной технический тренд. Но и он сводится к тому, что идёт автоматизация испытаний, тренд автоматизации оказывается и тут важнее. Инженерное обоснование требует огромных ресурсов, от выделения этих ресурсов нельзя уклониться, но эти ресурсы можно существенно снизить за счёт автоматизации.
Обоснование — это описание ожидаемой (или уже реальной) успешности системы. Альфа инженерного обоснования и все её подальфы — это подальфы описания, а рабочие продукты этих описаний — это assurance case, «папка обосновывающего дела», как папка судебного дела или дела/досье в какой-нибудь канцелярии, то есть документ (сегодня это обычно набор записей в базе данных, а не «листок бумаги» или даже «листок бумаги в электронной форме, например .pdf файл»).
В стандартах (QA, обоснование качества) чётко говорится, что обоснование успешности системы должно вестись в ходе всей разработки системы, это не «особая стадия перед выпуском в эксплуатацию», это просто часть разработки.
Испытания частей (юнит-тесты, которые пишут сами разработчики) ведутся незадолго до интеграционных испытаний, ибо нет никакого накопления новых частей перед интеграцией — введение новых версий системы в эксплуатацию происходит непрерывно, для софта это — по многу раз в день. После начала эксплуатации (это происходит с каждой фичей, с каждым «железным» изделием в серии, возможно каждое изделие в серии будет другое) эти же испытания/мониторинг ведутся так же регулярно (скажем, самодиагностика во время включения у оборудования, в котором есть хоть какой-то микропроцессор — это уже обычное дело, и помним про манифест прочности[9]: система всё время себя оценивает, что с ней происходит, чтобы быть прочной). А ещё в инженерных обоснованиях всё начинается с подготовки испытаний (современные варианты TDD, «что мы хотим от системы» документируется на языке испытаний), а затем ещё до испытаний документируют rationale, включая rationale на старт самого проекта создания системы — визионер должен принять информированное решение, согласиться с аргументами!
Инженерные обоснования — это метод, которым пользуются в ходе всего процесса инженерной разработки, укрепляя и укрепляя уверенность в том, что система получается успешной, она не только выполняет свою функцию, но и на неё можно положиться (система dependable, ещё одно часто используемое слово для выражения претензии/claim на успешность/success — и есть архитектурная характеристика dependability[10]).
При непрерывном вводе в эксплуатацию обосновывается в том числе каждая новая фича/инкремент, но также обосновывается, что вся система будет работать с этой новой фичей (и это даже не на двух уровнях, а на нескольких, помним про пирамиду тестирования). Так что в начале проекта формулируется претензия**/claim на успешность MVP, и дальше накапливаются материалы, её обосновывающие (подчинённые претензии в их иерархии/subclaims, аргументы и доказательства).**
В целом состояния, которые должна пройти альфа какой-то системы (или фичи/возможности/feature в очередном инкременте системы, там всё похоже — когда речь о системе, то чаще всего это просто MVP), могли бы включать следующие подсостояния (и для них нужны контрольные вопросы) по учёту инженерных обоснований:
- Система/фича будет нужна — претензия на нужность системы обоснована (инвестиционное или технико-экономическое обоснование/rationale концепции использования наличествует)
- Система/фича технически и экономически возможна — претензия на достижимость результатов проекта обоснована (концепция системы обоснована, архитектура обоснована, программа испытаний ясна)
- Система/фича спроектирована — представлены rationale по принятым проектным и архитектурным решениям, «код написан»/«информационная модель для завода подготовлена»/«решения для персонала предприятия (если система — предприятие) приготовлено» и включает тесты. Можно изготавливать и тестировать (работа с платформой разработки, которую предоставили инженеры платформы).
- Система/фича отмоделирована и испытана — получены свидетельства для обоснования успешности системы (и фичи, и всей системы).
- Система/фича успешна — получено полноценное инженерное обоснование успешности системы (для собранных свидетельств построены аргументы, обосновывающие претензию на успешность системы, аргументы приняты — это делается при обоснованиях успешности по compliance со стандартами, и изредка при обоснованиях продолжения проекта для топ-менеджеров, занимающихся стратегированием).
- Система/фича выведена из эксплуатации — получены свидетельства о том, что дальнейшая эксплуатация ранее успешной системы/фичи невозможна или невыгодна.
Как видите, тут можно считать, что для каких-то альф или подальф отслеживание обоснований можно сделать в форме подсостояний, требующих своих отдельных контрольных вопросов. Но можно говорить и о том, чтобы собрать это всё в отдельную подальфу инженерных обоснований — и сделать её подальфой нескольких альф, в том числе альфы коммерческой возможности (тем более что первый пункт прямо относится к коммерческой возможности ведения проекта по созданию системы или микропроекта одной команды по созданию/модификации новой фичи для системы). Альфы представляют собой не дерево, а граф, подальфа вполне может быть подальфой нескольких альф (это особо оговаривается и в стандарте OMG Essence). Учитываем, что:
- все состояния альф ссылаются друг на друга (это особо обговаривается в стандарте OMG Essence), это лишний повод обратить внимание организации проекта на важность удерживать отслеживание проектной ситуации по всем альфам в их взаимосвязи, нет требования иметь «ортогональные независимые» альфы. Если вам важно удерживать инженерные обоснования как особый приоритет в отслеживании, смело заводите подальфу для всех альф, для которых вы ожидаете проблемы с обоснованиями. Заботиться о формальной «ортогональности», «независимости» необязательно.
- онтологическая строгость определения альф и подальф не слишком большая. Каждая организация проекта адаптирует альфы к своей проектной ситуации до уровня мета-модели (предметной области из культуры, «как в учебниках» и предметной области «как у нас в проекте», то есть согласно регламентам и стандартам предприятия и предпочтениям сотрудников организации проекта). Так что нужно смотреть на то, как обычно устроены обоснования в вашей предметной области, но наше руководство и упомянутая в нём литература даст много идей о том, что может быть там пропущено или наоборот, что может быть лишним (особенно, если лишние обоснования лоббируют «безопасники», ибо они за обоснования не платят, им дёшево требовать лишнее).
Один из студентов-бакалавров, изучавший системную инженерию по предыдущей версии руководства на вопрос, что ему пригодилось больше всего из изученных методов мышления и работы, сказал: «я понял, что есть проверка и приёмка — и работы по этим методам в проекте обязательны. И я начал проверять всё, что выходит из моих рук перед тем, как отдавать результаты своей работы. И моя жизнь драматически изменилась к лучшему». Не жадничайте****тратить ресурсы на инженерные обоснования с самого начала до самого конца проекта**,** не ленитесь их делать, и ваша жизнь тоже драматически изменится к лучшему! Но будьте готовы, что на это уйдёт половина вашего времени, а то и больше! Ещё больше времени удастся сэкономить: за счёт того, что не будете тратить время на заведомо неуспешные проекты.
https://www.chiaramarletto.com/books/the-science-of-can-and-cant/ ↩︎
Дэвид Дойч, «Начало бесконечности. Объяснения, которые меняют мир» ↩︎
https://en.wikipedia.org/wiki/Hardware-in-the-loop_simulation ↩︎