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

Unit-тестирование скриншотами: преодолеваем звуковой барьер. Расшифровка доклада

Время на прочтение24 мин
Количество просмотров28K
Всего голосов 42: ↑40 и ↓2+38
Комментарии12

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

Привет. Спасибо за статью, интересно.

Раз уж речь зашла о Selenium, такой вопрос… А чем хуже в том же Selenium открывать необходимую страницу и получать HTML-source? Так будет честнее, чем собирать HTML «руками», как мне кажется.

Если необходимо сравнение не всей страницы, а только ее части, ее довольно легко вырезать уже на скриншоте, после выключения всей динамики.
чем хуже в том же Selenium открывать необходимую страницу и получать HTML-source? Так будет честнее, чем собирать HTML «руками», как мне кажется.

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


Если мы используем компонент в рамках блока или страницы, нам не нужны все его состояния – необходимо проверять, что его вид соответствует текущему набору данных и бизнес логике. А если что-то не так, нужна уверенность, что проблема не в самом компоненте, а в контексте (где он используется).


Можно загружать компонент/блок в Selenium, но для этого тоже нужно сделать сборку (bundle). А это не меньшее время. Запустить Selenium, открыть в нем страницу, чтобы получить HTML (а ведь нужен еще и CSS), по которому потом делать скриншот – оверхед, дополнительные траты ресурсов и времени. В целом можно, но получится сложнее и вряд ли быстрее.


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

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

Запустить Selenium, открыть в нем страницу, чтобы получить HTML (а ведь нужен еще и CSS), по которому потом делать скриншот – оверхед, дополнительные траты ресурсов и времени. В целом можно, но получится сложнее и вряд ли быстрее.


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

Но в целом Вы правы, это будет уже не unit-тестирование. Если описаный Вами метод приносит пользу, значит все не зря. :)

Спасибо за Ваш ответ.
Круто. Особенно понравилось про каретку. Я в свое время не додумался до такого. Только вот вопрос, это вообще правильно идеалогически тестировать что-то специально сгенерированное? Есть гарантия что это будет 100% то что надо? Про микросервис тоже странно мне показалось, проблема кросплатформенности и кроссбаузерности безспорно решена, а по-факту это тестирование в одном браузере.
сорри может я невнимательно прочитал, но тут был описан процесс обновления новых эталонных скриншотов?
это вообще правильно идеалогически тестировать что-то специально сгенерированное? Есть гарантия что это будет 100% то что надо?

100% гарантию сложно получить в чем бы то ни было. Мы стараемся получить максимально похожее на правду. Когда вносятся изменения или создаются новые тесты, то скриншоты проверяются как автором изменений, так и коллегами на code review. Если получаемые скриншоты похожи на "правду", то они становятся эталонными. Иначе исправляем проблему.


Про микросервис тоже странно мне показалось, проблема кросплатформенности и кроссбаузерности безспорно решена, а по-факту это тестирование в одном браузере.

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


сорри может я невнимательно прочитал, но тут был описан процесс обновления новых эталонных скриншотов?

Для обновления эталонный скриншотов используется тот же механизм, что и для обновления снепшотов в Jest (запускается с флагом -u или --updateSnapshot)

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

Кстати, не факт. Писал как-то свою реализацию Git и Mercurial. Так вот, Git никакие дельты не хранит, только gzip-ом сжимает.

Возможно зависит от реализации, которая может не использовать дельты.
Но когда делается клонирование стандартным клиентом, то в выводе можно увидеть упоминание дельт, например:


> git clone ssh://git@host/path/to/repo.git
Cloning into 'repo'...
remote: Counting objects: 138271, done.
remote: Compressing objects: 100% (47402/47402), done.
remote: Total 138271 (delta 95998), reused 129451 (delta 88469)
Receiving objects: 100% (138271/138271), 29.03 MiB | 6.84 MiB/s, done.
Resolving deltas: 100% (95998/95998), done.

В Wikipedia в секции Design отмечено:


Periodic explicit object packing
Git stores each newly created object as a separate file. Although individually compressed, this takes a great deal of space and is inefficient. This is solved by the use of packs that store a large number of objects delta-compressed among themselves in one file (or network byte stream) called a packfile. Packs are compressed using the heuristic that files with the same name are probably similar, but do not depend on it for correctness. A corresponding index file is created for each packfile, telling the offset of each object in the packfile. Newly created objects (with newly added history) are still stored as single objects and periodic repacking is needed to maintain space efficiency. The process of packing the repository can be very computationally costly. By allowing objects to exist in the repository in a loose but quickly generated format, Git allows the costly pack operation to be deferred until later, when time matters less, e.g., the end of a work day. Git does periodic repacking automatically but manual repacking is also possible with the git gc command. ...

В версии статьи на русском можно найти:


В репозитории иногда производится сборка мусора, во время которой устаревшие файлы заменяются на «дельты» между ними и актуальными файлами (именно так! актуальная версия файла хранится не-инкрементально, инкременты используются только для шагания назад), после чего данные «дельты» складываются в один большой файл, к которому строится индекс. Это снижает требования по месту на диске.
Действительно, был не прав. Вы открыли мне глаза :)

Это какая-то фантастика. Аплодирую стоя.

Я не понял, зачем все инлайнить и собирать страницу если потом это рендерит Пупеттир?

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий