Три сита в грамотном тестировании

Вечер.

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

Рассуждать хочу долго и о ситах.

Си́то — устройство для разделения сыпучих масс по величине их составляющих (зёрен, круп, песка и т.п.).

Сперва послушаем историков:

Человеческие жертвоприношения из ряда тестировщиков до и после релиза были распространённым явлением у менеджеров проектов, живших на полуострове Юкатан до нашествия конкистадоров. Тестировщиков приносили в жертву через повешение, утопление, отравление, избивание, а также посредством захоронения заживо в груде документации по проекту.

Наиболее жестоким видом жертвоприношения являлось, как и у девелоперов, вспарывание живота и вырывание из груди ещё бьющегося сердца тестировщика, который пропустил баг.

В жертву приносились как тестировщики, перекупленные в ходе войн представители других племён (корпораций), так и члены собственной группы, в том числе и высший слой, сениоры.

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

Итак, утопление.

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

Как все это добро достать?

Любое движение водолаза по дну такого жилищного массива баламутит всю грязь, и видимость склоняется к нулю.

А водолаз без движения уподобляется тем, кто уже никогда не выныривал из этого озера.

Выход — использовать насосы с ситами разного калибра. Положим три сита друг на друга. Наверху будет сито с самыми крупными ячейками. В середине — среднее. Снизу — самое мелкоячеистое.

Насос начинает выкачивать грязь из подземного озера. Грязь вываливается на сита, и археологам надо только подставлять свои любопытные руки под это месиво.

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

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

Сделаем музыкальную паузу.


Прочти, раскрась, запомни:

Тестирование в Agile-way особенно сильно тем, что всё, что можно автоматизировать, автоматизируется до и во время разработки, а не отдельно от неё.


Продолжим сосать мартини и рассуждать.

Каждое «сито» в девелопменте имеет свое имя.

  1. Test Driven Development — сито мелкоячеистое.
  2. Acceptance Test Driven Development — сито среднеячеистое.
  3. Functional Testing — сито крупноячеистое.

Первые два сита — епархия программистов. Сам я существенно разбираюсь только в третьей теме.

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

Рассуждать о трех ситах следует абстрактно, с точки зрения процесса, не вдаваясь в детальки.

Например, понятие сита — уже абстракция.

Абстрактность тут нужна потому, что большинству людей понятие TDD поначалу не «поддаётся», и неимоверно трудным оказывается процесс понимания всего этого. Но когда во все врубился и вгрыззся, становится странным, что до сих пор все это не использовал…

Unit testing

Внятный пример — LoveUT.

Цитата из статьи:

Тихо мурлыкая под нос, Анна продолжает кодировать свой класс.

Суть «разработки через тестирование» проста:

  • Сначала программист пишет тест для проверки функциональности, которую собирается написать. Этот тест называется «unit-test», потому, что он проверяет только одну единицу функционала.
  • Затем программист пишет функциональность.
  • И постоянно проверяет ее посредством ранее написанного, специально для нее, теста.
  • Как только функционал удовлетворяет требованиям этого теста, разработка функции считается завершенной. Переходим к следующей.
  • Во время разработки других функций, которые связаны с уже существующей, программисту можно и следует запускать юнит-тесты чаще, чем его легкие всасывают в себя один литр воздуха. Если все «горит зеленым» — программист счастлив. Если красное — увы.

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

Особенности юнит-тестов:

  • Неподготовленный человек не может их читать и понимать, не видя код.
  • Уровень абстракции в юнит-тестировании неимоверно нулевой. Тесты обрабатывают определенный код, и будучи отстраненными от кода просто не находят смысла в своем существовании. Цель юнит-тестирования — изолировать отдельные части программы и показать, что по отдельности эти части — работоспособны.
  • Когда функция меняется, юнит-тесты меняются в первую очередь.

Дальше надо сидеть рядом и показывать, как это работает. Этот момент в одиночку вряд ли одолеть, поэтому пропускаем подробности и едем дальше.

Acceptance testing

Внятное описание на английском языке: wikipedia.org.

Акксептанс-тесты — второй шаг после юнит-тестов. Это уже существенный уровень абстрактности от кода.

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

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

Особенности акксептанс-тестов:

  • Неподготовленный человек вполне может их читать и понимать, не видя код.
  • Уровень абстракции в юнит-тестировании неимоверно большой. Тесты обрабатывают не определенный код, а определенные абстракции, которые должны быть воплощены в коде.
  • Функция меняется как угодно. Акксептанс-тесты для нее не меняются.

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

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

Для сочувствующих agile development уточняем: акксептанс-тесты проверяют юзер-сториз. А юзер-сториз — вне кода, вне технологий. Получается, что у нас есть high level tests, которые запускаются нажатиями кнопок.

Пример, пример!

Приводится стандартный пример из учебного проекта, который видит каждый, кто умудряется запустить FitNesse.

В примере проверялась такая юзер-стори:

«User want to perform money operation from my account to another one to pay/receive money to/from another User»

посредством следующего приемочного критерия:

«After money operation one of the accounts is increased and another is decreased by the same amount of money».

Критерии акксептанс-тестов задают (а в идеальном мире — пишут) заказчики софта, а не разработчики. Например, пресловутый заказчик просит* следующего:

  • в базе должны существовать аккаунты разных юзеров.
  • у каждого юзера на счету есть какие-то деньги.
  • после нажатия кнопки «Сделать офигенно», со счета юзера №1 на счет юзера №2 должны передаваться какие-то суммы.
    * имхо, неимоверно удачно выбранный глагол.

Как выглядит такой тест, написанный в wiki-разметке в фреймворке «FitNesse»

FitNesse
Тесты в FitNesse читаемы и понятны

Как эта таблица выглядит в формате wiki:

Make sure that there are no accounts in the bank.

!|ensure|clean accounts|

Create two different accounts.

!|create account for user|Bob|with amount|5|

!|create account for user|John|with amount|7|

и тд

Как это выглядит после прогона теста:

FitNesse рапортует о выполнении задач партии
FitNesse рапортует о выполнении задач партии

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

Предварительное резюме

Тестирование на уровне юнит-тестов показывает, что кусочки кода по-отдельности работают, как ожидалось.

Тестирование на уровне приемочных-тестов показывает, что кусочки кода по-отдельности работают, как ожидалось, а также то, что взаимосвязи между ними работают и выполняются, как ожидалось.

Сравним это с муравьями. Пропустим муравьев через мелкое сито. Если каждый в отдельности муравей не хромает и бодро шевелит своими шестью лапками — это отличный муравей. Но это не гарантирует нам то, что муравейник, большая система взаимоотношений между муравьями, будет работать.

Систему взаимоотношений мы начинаем проверять средним ситом. В ячейки попадают по-несколько муравьев, например, отсортированных по принципам выполнения задач. Отдельно — фуражиры, отдельно — охранники, отдельно — муравьи-мамки, отдельно — все остальное. Но сцепленное между собой по какой-то логике.

Functional testing

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

Тут мы руками или головой проверяем, что будет, если мимо муравейника проедет танк, и кто победит, если муравьи нападут на танк. Ставлю сто баксов на муравьев…

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

Тут мы узнаем, что бывает, если муравьев в системе слишком много или слишком мало.

Это можно делать как всеми органами осязания, воззрения и осмысления, так и заранее документируя свои действия (тест-кейсы).

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

Окончательное прозрение

Если

  1. проект только начинается
  2. разработка ведется в стиле on-going (неизвестны ни конечный результат, ни дата полного финала)
  3. код пишется «с нуля»
  4. заказчик умеет работать в agile-стиле
  5. разработчики умеют работать в agile-стиле
  6. инженеры «болеют» тестированием
  7. все три сита постоянно применяются в процессе разработки

тогда

  1. проект вполне может завершиться выпуском идеального продукта
  2. все риски, которые влечет неполное тестирование, могут быть предупреждены и преодолены
  3. скорость разработки возрастает в неимоверные разы
  4. каждый гребёт свою кучу денег и убегает домой пить пиво и смотреть футбол с любимой женой.

Один ответ на “Три сита в грамотном тестировании”

Добавить комментарий