Рубрики
Без рубрики

Утверждения, которые считаются вредными

Утверждения – это механизм проверки в модульных тестах. Однако, когда речь идет о тестировании интерфейсов, особенно парней, я считаю их токсичными. К счастью, есть многообещающая альтернатива. С пометкой “тестирование”, “java”, “обсуждение”, “кодирование”.

Утверждения – это механизм проверки в модульных тестах. Однако, когда речь идет о тестировании интерфейсов, особенно парней, я считаю их токсичными. К счастью, есть многообещающая альтернатива.

JUnit имел огромный успех, будучи единственной наиболее используемой библиотекой во всей Java. И JUnit принес с собой знаменитый Assert.assert … заявление. Этот механизм предназначен для того, чтобы проверять только одну вещь одновременно изолированно. И при тестировании отдельного модуля это наиболее разумный подход: мы хотим игнорировать как можно больше изменчивого контекста. И мы хотим сосредоточиться на идеальной проверке только одного аспекта только тестируемого устройства. Это создает максимально длительные тесты. Если тест зависит только от одного аспекта кода, то его нужно только изменить если этот аспект изменится . Утверждения – это естественный и интуитивно понятный механизм для достижения этой цели. Находясь “внутри программного обеспечения во время тестирования, где практически все внутренние компоненты так или иначе подвергаются воздействию, все остальное не имело бы смысла.

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

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

Практический пример

Представьте, что вы хотите протестировать один аспект кода – подсчет количества товаров, которые когда-либо покупал один пользователь. На уровне единицы измерения все, что вам нужно, – это пользовательский объект и некоторые связанные с ним элементы или транзакции. В зависимости от сложности системы вы можете создавать эти объекты либо по требованию, либо имитировать их. Затем вы можете протестировать только код, который подсчитывает элементы.

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

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

Представьте себе, всего на секунду, что мы могли бы каким-то образом смягчить умножение эффектов. Тогда мы могли бы проверить полное состояние системы, а не отдельные аспекты. Мы могли бы проверить все сразу!

Итак, поскольку интерфейсы хрупки, мы теперь хотим включить больше контекста, что делает наши тесты еще более хрупкими? Потому что вместо того, чтобы зависеть от отдельных аспектов, тест теперь зависит от всего сразу? Кому бы это понадобилось? Хорошо… все, кто хочет знать, изменился ли интерфейс. Если вы подумаете об этом, то тот же вопрос относится и к управлению версиями. Система контроля версий – это система, с помощью которой каждый раз, когда вы меняете что-либо в любом файле , вы должны вручную утвердить это изменение. Какое умножение усилий! Какая пустая трата времени! За исключением того, что не использовать его – очень плохая идея.

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

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

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

Конечно, я не первый, кому пришла в голову эта идея. В своей книге Эффективно работая с устаревшим кодом , Майкл Перс назвал этот подход тестированием характеристик , другие называют его тестированием золотого мастера . Сегодня есть две возможности: сравнение на основе пикселей и на основе текста. Поскольку сравнение на основе пикселей (часто называемое визуальным регрессионным тестированием) легко реализовать, существует множество инструментов . Для сравнения на основе текста существуют, по сути, два конкретных инструмента тестирования: Испытания на официальное утверждение и Текстовый тест

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

Как мы можем сдержать это умножение эффектов? Одна из возможностей заключается в создании дополнительного уровня абстракции, как это делается с помощью одностраничных объектов или объектных карт . Но для того, чтобы позже пожать плоды в виде сокращения усилий, если произойдет ожидаемое изменение, это требует предварительных усилий вручную. Согласно YAGNI , реализация этой абстракции “на всякий случай” на самом деле является плохой вещью.

Какие еще у нас есть возможности сдержать умножение эффектов? Выполняя рефакторинг в программировании, мы оказываемся в такой же ситуации. Один метод, вероятно, вызывается в десятках или даже сотнях мест. Поэтому при переименовании одного метода (пожалуйста, делайте это только во внутренних, не открытых API) нам также необходимо изменить каждое место, где вызывается этот метод. В некоторых случаях мы можем вывести эти места из абстрактного синтаксического дерева. В других случаях (файлы свойств, документация, …) мы должны полагаться на текстовый поиск и замену. Если мы что–то забываем или наблюдаем за чем-то, это часто проявляется только в определенных ситуациях – обычно при запуске программного обеспечения. Но для тестов это совсем другое дело. Потому что тесты, по определению, уже выполняют программное обеспечение. Таким образом, нам показывают все места, где что-то изменилось (например, из-за неудачных тестов). Теперь нам просто нужен механизм для “массового применения подобных изменений”.

Есть два разных вида изменений: различия в компоновке и различия в потоке .

Различия в компоновке

Если, например, кнопка входа в систему теперь называется “войти”, имеет другое внутреннее имя, XPath или xy-координаты, это разница в расположении. Различия в компоновке относительно легко устранить с помощью карт объектов.

Но, что удивительно, различия в компоновке также относительно легко устранить, если у нас будет больше контекста. Если мы знаем всю головоломку целиком, а не только отдельные фрагменты, мы можем создавать индивидуальные задания. Это обеспечивает очень надежное распознавание объектов.

Представьте, у нас есть форма, в которую добавляются некоторые элементы. И мы хотим распознать кнопку “Принять” для отправки формы. Если все, что касается кнопки, изменится, мы все равно сможем распознать ее, основываясь на индивидуальном назначении оставшихся неиспользуемых UI-компонентов.

И массовое применение этих изменений также легко. Мы можем просто применить каждое подобное изменение. Например. объедините все экземпляры изменения “Принять для сохранения” в одно изменение, которое необходимо просмотреть только один раз.

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

Таким образом, мы можем собрать XPath, имя, метку и пиксельные координаты. Если некоторые значения изменятся, у нас все равно останутся оставшиеся значения для идентификации элемента. А массовое нанесение делает это по-прежнему простым в обслуживании.

Различия в потоке

Иногда варианты использования или внутренние процессы программного обеспечения меняются. Это могут быть незначительные изменения (например, если требуется дополнительный шаг – заполнение captcha или сброс пароля). Иногда это серьезные изменения – рабочий процесс полностью меняется. В последнем случае, вероятно, проще переписать тесты. Но это случается редко. Чаще всего нам нужно просто немного адаптировать тесты.

Различия в потоке не могут быть устранены с помощью карт объектов. Вместо этого нам нужны другие формы абстракций: извлечение повторяющихся потоков в виде “функций или ” процедур и их повторное использование. Это может быть достигнуто с помощью объектов страницы, но требует ручного труда и правильной абстракции.

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

Пассивное обновление заключается в том, чтобы вместо этого указать ситуацию, которую нам нужно обновить, вместе с правилом о том, как обновлять. Таким образом, вместо того, чтобы находить все попытки входа в систему, мы указываем ситуацию: страница входа заполнена учетными данными и отображается captcha. Теперь мы добавляем правило о том, как обновить тестовый скрипт, который оказывается в такой ситуации – заполнение капчи. Мы делаем это, удаляя или вставляя отдельные действия или их комбинацию. Затем это обновление применяется пассивно, после выполнения тестов. Это означает, что мы, по сути, переворачиваем извлечение процедуры с ног на голову.

Такой подход имеет ряд преимуществ:

  1. Сохраняя умножение эффектов, этот подход требует меньших усилий для обновления ваших тестов.
  2. Создавая подробные правила, это позволяет быть более детализированным в обновлении.
  3. Это действительно влияет только на тесты, которые находятся в указанной ситуации во время выполнения – нет необходимости анализировать статический тестовый сценарий и интерпретировать, применима ли ситуация к этому тесту во время выполнения или вручную отлаживать его.
  4. Могут быть определены общие правила, применимые к различным ситуациям. Таким образом, у нас может быть правило, которое гласит: всякий раз, когда тест обнаруживает модальный диалог и только одну опцию (например, “ок”), щелкните эту опцию и продолжайте тест. Это делает наши тесты намного более устойчивыми к непредвиденным изменениям.

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

Мы уже реализовали этот подход для Java Swing. Теперь мы хотим создать инструмент с открытым исходным кодом, который будет способствовать широкому внедрению. Мы высоко ценим любую поддержку – оставляйте нам отзывы, поддерживайте нас или распространяйте информацию.

У нас есть кампания Kickstarter , которая позволяет вам финансировать нас. Поддержите нас, чтобы проголосовать за то, какая технология будет внедрена следующей. Или получите непревзойденную цену за премиум. Или просто быть крутым парнем, который может заявить: я поддержал их в 2017 году.

Спасибо!

Оригинал: “https://dev.to/roesslerj/assertions-considered-harmful-6pf”