Русский
Русский
English
Статистика
Реклама

Cypress

Пишем интеграционные тесты на фронтэнд и ускоряем релизы

29.06.2020 16:21:22 | Автор: admin
Всем привет! Меня зовут Вова, я фронтэндер в Тинькофф. Наша команда отвечает за два продукта для юридических лиц. О размерах продукта я могу сказать цифрами: полный регресс каждого из продуктов двумя тестировщиками проходит от трех дней (без влияния внешних факторов).

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

Последний пункт и стал темой моей статьи.

image

Пирамида тестирования


Как мы знаем, в пирамиде тестирования три уровня: unit-тесты, интеграционные тесты и e2e-тесты. Думаю, с юнитами знакомы многие, как и с e2e, поэтому чуть подробнее остановлюсь на интеграционных тестах.

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

Для написания интеграционных тестов мы используем Cypress. В этой статьей я не буду сравнивать его с другими фреймворками, скажу только, почему он оказался у нас:

  1. Очень подробная документация.
  2. Легкий debugging тестов (у Cypress для этого сделан специальный GUI с time-travel по шагам в тесте).

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

Начало пути


В самом начале для организации кода использовался Angular Workspace с одним приложением. После установки пакета Cypress в корне приложения появилась папка cypress с конфигурацией и тестами, на данном варианте мы остановились. При попытке подготовить в package.json скрипт, необходимый для запуска приложения и прогона поверх него тестов, мы столкнулись со следующими проблемами:

  1. В index.html были зашиты некоторые скрипты, которые не нужны в интеграционных тестах.
  2. Для запуска интеграционных тестов необходимо было убедиться, что сервер с приложением запущен.

Проблему с index.html решили через отдельную конфигурацию сборки назовем ее сypress, в которой указали кастомный index.html. Как это реализовать? Находим в angular.json конфигурацию вашего приложения, открываем секцию build, добавляем там отдельную конфигурацию для Cypress и не забываем указывать эту конфигурацию для serve-режима.

Пример конфигурации для build:

"build": {..."configurations": { // Другие конфигурации"cypress": {"aot": true,"index": "projects/main-app-integrations/src/fixtures/index.html","fileReplacements": [{"replace": "projects/main-app/src/environments/environment.ts","with": "projects/main-app/src/environments/environment.prod.ts"}]}}}

Интеграция с serve:

"serve": {..."configurations": { // Другие конфигурации"cypress": {"browserTarget": "main-app:build:cypress"}}}

Из основного: для cypress конфигурации мы указываем aot сборку и подменяем файлы с environment это необходимо для создания prod-like сборки при тестировании.

Итак, с index.html разобрались, осталось поднять приложения, дождаться окончания сборки и прогнать поверх него тесты. Для этого используем библиотеку start-server-and-test и на ее основе напишем скрипты:

"main-app:cy:run": "cypress run","main-app:cy:open": "cypress open","main-app:integrations": "start-server-and-test main-app:serve:cypress http://localhost:8808/app/user/ main-app:cy:run","main-app:integrations:open": "start-server-and-test main-app:serve:cypress http://localhost:8808/app/user/ main-app:cy:open"

Как можно заметить, тут два типа скриптов: open и run. Режим open открывает GUI самого Cypress, где можно переключаться между тестами и использовать time-travel. Режим run это просто прогон тестов и получение конечного результата этого прогона, отлично подходит для запуска в CI.

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

Монорепозиторий


У описанного подхода есть очень заметная проблема: если в репозитории два и более приложений, то подход с одной папкой нежизнеспособен. Так и произошло у нас. Но случилось это довольно интересным способом. В момент внедрения Cypress мы переезжали на NX, а этот красавец из коробки дает возможность работать с Cypress. Каков принцип работы в нем:

  1. У вас есть приложение, например main-app, рядом с ним создается приложение main-app-e2e.
  2. Переименуйте main-app-e2e в main-app-integrations вы восхитительны.

Теперь вы можете прогонять интеграционные тесты одной командой ng e2e main-app-integrations. NX автоматически поднимет main-app, дождется ответа и прогонит тесты.

К сожалению, остались в стороне те, кто сейчас использует Angular Workspace, но ничего страшного, у меня есть рецепт и для вас. Файловую структуру будем использовать как и у NX:

  1. Создаем рядом с вашим приложением папку main-app-integrations.
  2. Создаем в ней папку src и заносим в нее содержимое папки cypress.
  3. Не забываем перенести cypress.json (изначально он появится в корне) в папку main-app-integrations.
  4. Правим cypress.json, указывая пути до новых папок с тестами, плагинами и вспомогательными командами (параметры integrationFolder, pluginsFile и supportFile).
  5. Cypress умеет работать с тестами в любых папках, для указания папки используется параметр
    project, поэтому меняем команду с cypress run/open на cypress run/open -project ./projects/main-app-integrations/src.

Решение под Angular Workspace максимально похоже на решение для NX, за исключением того, что папку создаем руками и она не является одним из проектов в вашем монорепозитории. В качестве альтернативы можно напрямую использовать билдер от NX для Cypress (пример репозитория на NX с Cypress, там можно подсмотреть итоговое использование nx-cypress билдера внимание на angular.json и проект
cart-e2e и products-e2e).

Visual Regressing


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

В качестве решения была взята библиотека cypress-image-snapshot. Внедрение не отняло много времени, и вот спустя 20 минут мы получили первый скриншот нашего приложения размером 1000 600 px. Радости было много, ведь интеграция и использование были слишком простыми, а полученная польза могла быть огромной.

После генерации пяти эталонных скриншотов мы запустили проверку в CI, как итог билд развалился. Оказалось, что скриншоты, созданные с помощью команд open и run, отличаются. Решение было довольно простым: снимать скриншоты только в CI-режиме, для этого убрали снятие скриншотов в local-режиме, например так:

Cypress.Commands.overwrite('matchImageSnapshot',(originalFn, subject, fileName, options) => {if (Cypress.env('ci')) {return originalFn(subject, fileName, options);}return subject;},);

В данном решении мы смотрим на параметр env в Cypress, установить его можно разными путями.

Шрифты


Локально тесты начали проходить при повторном запуске, пытаемся снова запустить их в CI. Результат можно увидеть ниже:



Довольно просто заметить разницу в шрифтах на diff-скриншоте. Эталонный скриншот был сгенерирован на macOS, а в CI на агентах установлен Linux.

Неправильное решение


Подобрали один из стандартных шрифтов (вроде это был Ubuntu Font), который давал минимальный попиксельный diff, и применили этот шрифт для текстовых блоков (сделано в
index.html, который предназначался только для cypress-тестов). Затем повысили общий diff до 0,05% и попиксельный diff до 20%. С такими параметрами мы прожили неделю до первого случая, когда потребовалось изменить текст в компоненте. В итоге билд остался зеленым, хотя скриншот мы не обновили. Текущее решение оказалось бесполезным.

Правильное решение


Исходная проблема была в разных окружениях, решение в принципе напрашивается само собой Docker. Для Cypress уже есть готовые docker-образы. Там есть разные вариации образов, нас интересует included, так как Cypress в нем уже включен в образ и не будет каждый раз заново происходить скачивание и распаковка Cypress binary (GUI Cypress запускается через бинарный файл, и скачивание вместе с распаковкой занимает больше времени, чем скачивание docker-образа).
На основе included docker-образа делаем свой docker-контейнер, для этого у себя мы сделали файл integration-tests.Dockerfile с подобным содержимым:

FROM cypress:included:4.3.0COPY package.json /app/COPY package-lock.json app/WORKDIR /appRUN npm ciCOPY / /app/ENTRYPOINT []

Хочется отметить обнуление ENTRYPOINT, это связано с тем, что он задан по умолчанию в образе cypress/included и указывает на команду cypress run, что не дает нам использовать другие команды. Также разбиваем наш dockerfile на слои, чтобы при каждом перезапуске тестов не выполнять повторно npm ci.

Добавляем .dockerignore файл (если его нет) в корень репозитория и в нем обязательно указываем node-modules/ и */node-modules/.

Для запуска в Docker наших тестов напишем bash-скрипт integration-tests.sh со следующим содержимым:

docker build -t integrations -f integration-tests.Dockerfile .docker run --rm -v $PWD/projects/main-app-integrations/src:/app/projects/main-app-integrations/src integrations:latest npm run main-app:integrations

Краткое описание: билдим наш docker-контейнер integration-tests.Dockerfile и указываем volume на папку с тестами, чтобы была возможность получить созданные скриншоты из Docker.

Снова шрифты


После решения проблемы, описанной в предыдущей главе, в билдах наступило затишье, но примерно через день мы столкнулись со следующей проблемой (слева и справа скриншоты одного компонента, сделанные в разное время):



Думаю, самые внимательные заметили, что не хватает заголовка в попапе. Причина очень простая не успел загрузиться шрифт, так как не был подключен через assets, а находился на CDN.

Неправильное решение


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

Правильное решение


Было решено начать предзагружать все необходимые шрифты для теста в
index.html для cypress-конфигурации, выглядело это примерно так:

<linkrel="preload"href="...."as="font"type="font/woff2"crossorigin="anonymous"/>

Число падений тестов из-за не успевших загрузиться шрифтов снизилось до минимума, но не до нуля: все равно иногда шрифт не успевал загрузиться. На помощь пришло решение из KitchenSink самого Cypress waitForResource.
В нашем случае, так как уже была подключена предзагрузка шрифтов, мы просто переопределили команду visit в Cypress, в итоге она не просто навигируется на страничку, но и ждет загрузку указанных шрифтов. Также хотелось бы дополнить, что waitForResource решает проблему не только шрифтов, но и любой загружаемой статики, например картинок (из-за них у нас также ломались скриншоты, и waitForResource отлично помогло). После применения данного решения проблем со шрифтами и любой загружаемой статикой не было.

Анимации


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

Первое решение


Самое простое, что нам пришло в голову на начальном этапе: перед созданием скриншота останавливать браузер на определенное время, чтобы анимации успели завершиться. Шли по цепочке 100ms, 200ms, 500ms и в итоге 1000ms. Оглядываясь назад, я понимаю, что это решение изначально было ужасным, но хотелось именно предостеречь вас от такого же решения. Почему ужасным? Время анимаций разное, агенты в CI тоже могут подтупливать иногда, из-за чего любое время ожидания стабилизации страницы от раза к разу было разным.

Второе решение


Даже с ожиданием в 1 секунду страница не всегда успевала стать стабильной. После небольшого ресерча нашли инструмент у Angular Testability. Принцип основан на отслеживании стабильности ZoneJS:

Cypress.Commands.add('waitStableState', () => {return cy.window().then(window => {const [testability]: [Testability] = window.getAllAngularTestabilities();return new Cypress.Promise(resolve => {testability.whenStable(() => {resolve();}, 3000);});});});

Таким образом при создании скриншотов у нас вызывались две команды: cy.wait(1000) и cy.waitStableState().

С тех пор не было ни одного рандомно упавшего скриншота, но давайте вместе посчитаем, сколько времени тратилось на простаивание браузера. Предположим, у вас в тесте делается 5 скриншотов, для каждого есть стабильное время ожидания в 1 секунду и какое-то рандомное время, предположим 1,5 секунды в среднем (я не замерял среднее значение в реальности, поэтому взял из головы по собственным ощущениям). В итоге для создания скриншотов в тесте мы тратим дополнительно 12,5 секунды. Представим, что вы уже написали 20 тестовых сценариев, где в каждом тесте не менее 5 скриншотов. Получаем, что переплата за стабильность ~4 минуты при имеющихся 20 тестах.

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

Текущее решение


Начнем с Angular-анимаций. Наш любимый фреймворк во время анимации на элементе DOM навешивает класс ng-animating. Это и стало ключом нашего решения, ведь теперь надо убедиться, что на элементе сейчас нет класса анимации. В итоге это вылилось в такую функцию:

export function waitAnimation(element: Chainable<JQuery>): Chainable<JQuery> { return element.should('be.visible').should('not.have.class', 'ng-animating');}

Кажется, ничего сложного, но именно это легло в основу наших решений. На что хочется обратить внимание в таком подходе: делая скриншот, вы должны понимать, анимация какого элемента может сделать ваш скриншот нестабильным, и перед созданием скриншота добавить assertion, который проверит, что элемент не анимируется. Но анимации также могут быть и на CSS. Как говорит сам Cypress, любые assertion на элементе ждут окончания анимации на нем подробнее тут и тут. То есть суть подхода в следующем: у нас есть анимируемый элемент, добавляем на него assertion should(be.visible)/should(not.be.visible) и Cypress сам дождется окончания анимации на элементе (возможно, кстати, решение с ng-animating не нужно и достаточно только проверок Cypress, но мы пока что используем утилиту waitAnimation).

Как сказано в самой документации, Cypress проверяет изменение позиции элемента на странице, но не все анимации про изменение позиции, есть также fadeIn/fadeOut-анимации. В этих случаях принцип решения тот же: проверяем, что элемент виден / не виден на странице.

При переезде с решения cy.wait(1000) + cy.waitStableState() на waitAnimation и Cypress Assertion пришлось потратить ~2 часа времени на стабилизацию старых скриншотов, но как итог мы получили +2030 секунд вместо +4 минут на время выполнения тестов.На данный момент мы тщательно подходим к ревью скриншотов: проверяем, что они выполнены не во время анимации элементов DOM и добавлены проверки в тесте на ожидание анимации. Например, мы часто добавляем отображение скелетонов на странице, пока данные не загрузились. Соответственно, на ревью сразу же прилетает требование, что при создании скриншотов в DOM не должен присутствовать скелетон, так как на нем находится анимация плавного исчезновения.

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

Размер скриншотов


Возможно, вы заметили интересную особенность: разрешение скриншотов по умолчанию 1000 600 px. К сожалению, есть проблема с размером окна браузера при запуске в Docker: даже если вы через Cypress поменяете размер viewportа, это не поможет. Мы нашли решение для браузера Chrome (для Electron не удалось быстро найти работающее решение, а предложенное в данном issue у нас не завелось). Для начала надо сменить браузер для запуска тестов на Chrome:

  1. Не для NX делаем с помощью аргумента --browser chrome при запуске команды cypress open/run и для run-команды указываем параметр --headless.
  2. Для NX в конфигурации проекта в angular.json с тестами указываем параметр browser: chrome, и для конфигурации, которая будет запускаться в CI, указываем headless: true.

Теперь делаем правки в plugins и получаем скриншоты размером 1440 900 px:

module.exports = (on, config) => {on('before:browser:launch', (browser, launchOptions) => {if (browser.name === 'chrome' && browser.isHeadless) {launchOptions.args.push('--disable-dev-shm-usage');launchOptions.args.push('--window-size=1440,1200');return launchOptions;}return launchOptions;});};

Даты


Тут все просто: если где-то отображается дата, связанная с текущей, снятый скриншот сегодня завтра упадет. Фиксим просто:

cy.clock(new Date(2025, 11, 22, 0).getTime(), ['Date']);

Теперь таймеры. Мы не стали заморачиваться и используем опцию blackout при создании скриншотов, например:

cy.matchImageSnapshot('salary_signing-several-payments', {blackout: ['.timer'],});

Flaky-тесты


Используя приведенные выше рекомендации, вы сможете добиться максимальной стабильности тестов, но не 100%, ведь на тесты влияет не только ваш код, но и окружение, в котором они запускаются.

В итоге какой-то процент тестов будет изредка падать, например из-за проседаний производительности агентов в CI. В первую очередь стабилизируем тест с нашей стороны: добавляем необходимые assertion перед снятием скриншотов, но на период починки таких тестов можно использовать retry упавших тестов с помощью cypress-plugin-retries.

Прокачиваем CI


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

  1. Команда npm ci.
  2. Поднятие приложения в aot-режиме.
  3. Запуск интеграционных тестов.

Взглянем на первый и второй пункт и поймем, что похожие шаги выполняются в другом вашем билде в CI билд со сборкой приложения.
Основное отличие это запуск не ng serve, а ng build. Таким образом, если сможем получить уже собранное приложение в билде с интеграционные тестами и поднять сервер с ним, то мы сможем сократить время выполнения билда с тестами.

Почему нам это потребовалось? Просто приложение у нас большое и выполнение
npm ci + npm start в aot-режиме на агенте в CI занимало ~15 минут, что в принципе требовало больших усилий от агента, и еще поверх этого запускались интеграционные тесты. Предположим, у вас уже написано 20+ тестов и на 19-м тесте у вас падает браузер, в котором прогоняются тесты, из-за большой нагрузки на агент. Как вы понимаете, перезапуск билда это снова ожидание установки зависимостей и запуска приложения.

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

Сервер со статикой


Нам нужна замена ng serve по поднятию сервера с нашим приложением. Вариантов много, начну с нашего первого angular-http-server. В его настройке нет ничего сложного: устанавливаем зависимость, указываем, в какой папке лежит наша статика, указываем, на каком порту поднять приложение, и радуемся.

Этого решения нам хватило на целых 20 минут, а потом мы поняли, что какие-то запросы хотим проксировать на тестовый контур. Подключить проксирование для angular-http-server не получилось. Конечным решением стало поднятие сервера на Express. Для решения задачи использовался сам express и express-http-proxy. Раздавать нашу статику будем с помощью
express.static, в итоге получается скрипт, похожий на этот:

const express = require('express');const appStaticPathFolder = './dist';const appBaseHref = './my/app';const port = 4200;const app = express();app.use((req, res, next) => {const accept = req.accepts().join().replace('*/*', '');if (accept.includes('text/html')) {req.url = baseHref;}next();});app.use(appBaseHref, express.static(appStaticPathFolder));app.listen(port);

Интересным моментом здесь является то, что перед прослушиванием роута по baseHref приложения мы также обрабатываем все запросы и ищем запрос на index.html. Это сделано для случаев, когда в тестах осуществляется переход на страницу приложения, путь которой отличается от baseHref. Если не сделать данный трюк, то при переходе на любую страницу вашего приложения, кроме главной, будет прилетать 404 ошибка. Теперь добавим щепотку проксирования:

const proxy = require('express-http-proxy');app.use('/common',proxy('https://qa-stand.ru', {proxyReqPathResolver: req => '/common' + req.url,}),);

Чуть подробнее рассмотрим происходящее. Есть константы:

  1. appStaticForlderPath папка, где находится статика вашего приложения.
  2. appBaseHref возможно, у вашего приложения есть baseHref, если нет можно указать /.

Проксируем же мы все запросы, начинающиеся на /common, причем при проксировании сохраняем тот же путь, который был у запроса, с помощью настройки proxyReqPathResolver. Если ее не использовать, то все запросы будут просто идти наhttps://qa-stand.ru.

Кастомизация index.html


Нам нужно было решить проблему с кастомным index.html, который мы использовали при ng serve приложения в режиме Cypress. Напишем простой скрипт на node.js. Исходными параметрами у нас был index.modern.html, требовалось превратить его в index.html и удалить оттуда ненужные скрипты:

const fs = require('fs');const appStaticPathFolder = './dist';fs.copyFileSync(appStaticPathFolder + '/index.modern.html', appStaticPathFolder + '/index.html');fs.readFile(appStaticPathFolder + '/index.html', 'utf-8', (err, data) => {const newValue = data.replace('<script type="text/javascript" src="http://personeltest.ru/aways/habr.com/auth.js"></script>','',).replace('<script type="text/javascript" src="http://personeltest.ru/aways/habr.com/analytics.js"></script>','',);fs.writeFileSync(appStaticPathFolder + '/index.html', newValue, 'utf-8');});

Скрипты


Очень не хотелось для запуска тестов в CI делать еще раз npm ci всех зависимостей (ведь это уже делалось в таске с билдом приложения), поэтому появилась идея создать отдельную папку под все эти скрипты со своим package.json. Назовем папку, например, integration-tests-scripts и закинем туда три файла: server.js, create-index.js, package.json. Первые два файла были описаны выше, разберем теперь содержимое package.json:

{"name": "cypress-tests","version": "0.0.0","private": true,"scripts": {"create-index": "node ./create-index.js","main-app:serve": "node ./server.js","main-app:cy:run": "cypress run --project ./projects/main-app-integrations ","main-app:integrations": "npm run create-index && start-server-and-test main-app:serve http://localhost:4200/my/app/ main-app:cy:run"},"devDependencies": {"@cypress/webpack-preprocessor": "4.1.0","@types/express": "4.17.2","@types/mocha": "5.2.7","@types/node": "8.9.5","cypress": "4.1.0","cypress-image-snapshot": "3.1.1","express": "4.17.1","express-http-proxy": "^1.6.0","start-server-and-test": "1.10.8","ts-loader": "6.2.1","typescript": "3.8.3","webpack": "4.41.6"}}

В package.json присутствуют только зависимости, необходимые для прогона интеграционных тестов (с поддержкой typescript и скриншот-тестирования) и скрипты по запуску сервера, созданию index.html и известный из главы по запуску интеграционных тестов в Angular Workspace start-server-and-test.

Запуск


Оборачиваем выполнение интеграционных тестов в новый Dockerfile integration-tests-ci.Dockerfile:

FROM cypress/included:4.3.0COPY integration-tests-scripts /app/WORKDIR /appRUN npm ciCOPY projects/main-app-integrations /app/projects/main-app-integrationsCOPY dist /app/distCOPY tsconfig.json /app/ENTRYPOINT []

Суть проста: копируем и разворачиваем папку integration-tests-scripts в корень приложения и копируем все, что необходимо для запуска тестов (у вас этот набор может отличаться). Основные отличия от предыдущего файла в том, что мы не копируем все приложение внутрь docker-контейнера, просто минимальная оптимизация времени выполнения тестов в CI.

Создаем файл integration-tests-ci.sh со следующим содержимым:

docker build -t integrations -f integration-tests-ci.Dockerfile .docker run --rm -v $PWD/projects/main-app-integrations/src:/app/projects/main-app-integrations/src integrations:latest npm run main-app:integrations

Во время запуска команды с тестами корневым станет package.json из папки integration-tests-scripts и в нем запустится команда main-app:integrations. Соответственно, так как эта папка развернется в корень, то и пути до папки со статикой вашего приложения нужно указывать с мыслью, что запускаться все будет из корня, а не из папки integration-tests-scripts.

Хочу также сделать небольшую ремарку: конечный bash-скрипт для запуска интеграционных тестов по мере его эволюции я называл по-разному. Так делать не нужно, это было сделано только для удобства чтения этой статьи. У вас должен всегда оставаться один файл, например integration-tests.sh, который вы уже развиваете. Если у вас несколько приложений в репозитории и их способы подготовки различаются, можно разруливать либо переменными в bash, либо разными файлами под каждое приложение зависит от ваших потребностей.

Итог


Информации было много думаю, теперь стоит подвести итог на основе написанного выше.
Подготовка средств для локального написания и прогона тестов с щепоткой скриншот-тестирования:

  1. Ставим зависимость для Cypress.
  2. Подготавливаем папку с тестами:
    1. Angular Single Application оставляем все в папке cypress.
    2. Angular Workspace создаем папку имя приложения-integrations рядом с приложением, на которое будут гоняться тесты, и переносим в нее все из папки cypress.
    3. NX переименовываем проект из имя приложения-e2e в имя-приложения-integrations.

  3. Кастомная cypress-сборка для поднятия приложения делаем в build-разделе конфигурацию Cypress, указываем там aot, подмену на свой отдельный index.html, подмену файла environment на prod-файл и указываем в serve-разделе сборку Cypress (данный пункт необходим, если вам нужны какие-то отличия от prod-сборки, в противном случае можно пропустить).
  4. Скрипты по запуску и прогону тестов:
    1. Angular Single Application готовим скрипт с serve-ом cypress-сборки и запуску тестов, комбинируем все это с помощью start-server-and-test.
    2. Angular Workspace аналогично с Angular Single Application, только указываем путь до тестов при запуске cypress run/open.
    3. NX запускаем тесты с помощью команд ng e2e.

  5. Подмешиваем скриншот-тестирование:
    1. Добавляем зависимость cypress-image-snapshot.
    2. Переопределяем стандартную команду по сравнению скриншотов для запуска ее только в CI.
    3. В тестах не делаем скриншотов на рандоме. Если скриншоту предшествует анимация, обязательно ждем ее например, добавляем Cypress Assertion на анимируемый элемент.
    4. Дату мокируем через cy.clock либо используем опцию blackout при снятии скриншота.
    5. Любую подгружаемую статику в runtime ожидаем через кастомную команду cy.waitForResource (картинки, шрифты и т. д.).

  6. Оборачиваем все это в Docker:
    1. Готовим Dockerfile.
    2. Создаем bash-файл.


Прогоняем тесты поверх собранного приложения:

  1. В CI учимся прокидывать между билдами артефакты собранного приложения (остается на вас).
  2. Готовим папку integration-tests-scripts:
    1. Скрипт по поднятию сервера вашего приложения.
    2. Скрипт по изменению вашего index.html (если вас устраивает изначальный index.html можно скипать).
    3. Добавляем в папку package.json с необходимыми скриптами и зависимостями.
    4. Готовим новый Dockerfile.
    5. Создаем bash-файл.


Полезные ссылки


  1. Angular Workspace + Cypress + CI в данном примере я создал базу для Angular Workspace приложения с накрученным CI на основе скриптов, описанных в статье (без поддержки typescript).
  2. Cypress обратите внимание на раздел trade-offs.
  3. Start-server-and-test запуск сервера, ожидание ответа и запуск тестов.
  4. Cypress-image-snapshot библиотека для скриншот-тестирования.
  5. Cypress recipes готовые наработки по Cypress, чтобы не изобретать свои велосипеды.
  6. Flaky Tests статья про нестабильные тесты, в конце есть ссылка на статью от Google.
  7. Github Action Cypress дает возможность использовать предустановленные конфиги для GitHub Action, в README куча примеров, из интересного встроенная поддержка wait-on. Также есть пример с docker.
Подробнее..

Никогда не писали автотесты? Попробуйте Cypress

28.09.2020 20:13:28 | Автор: admin

Никогда не писали автотесты? Попробуйте Cypress


Автотесты на Cypress
Первое впечатление и встречающиеся проблемы


Дмитрий Кочергин, Lead Software Developer Luxoft

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

Статья будет интересна всем, кто интересовался автотестированием с нуля на JS, но боялся спросить.

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

Быстрый поиск по интернету дал более молодые и перспективные инструменты: WebDriver.IO, Pupeteer (а сейчас лучше Playwright) и Cypress. Выбрал последний, купился на красивые обещания и несколько комментов из холиваров по лучшим инструментам для автотестов.

Так выглядит окно браузера запущенного теста. Слева выполненные команды теста и статус, справа просмотр приложение во время исполнения теста:
image

Вот так выглядит код теста (в Cypress весь код на JS, а селекторы это обычные CSS селекторы):
image

В runtime выглядит так:
image

Пока искал инструмент для автотестов в голове у меня была картинка, что тесты можно создавать прямо в браузере, чтобы просто нажал на запись скрипта система записала мои действия (CSS селекторы элементов, на которые я нажимал). Потом я смотрю сгенерированный тест, подправляю селекторы если нужно, и сохраняю в Test Suite. Эх

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

Из других параметров, которые для меня были важны я критически не хотел тратить много времени ни на написание (опять про запись теста в браузере), ни на поддержку тестов. Для моих целей этого было вполне достаточно. Просто для информации o Cypress: с момента, как я впервые открыл сайт Cypress, до первого теста, который логинится в приложение прошел 1 час.

Итак, Cypress, первая страница фреймворка говорит нам что это JavaScript End to End Testing Framework (cypress.io). Далее бегло читаем документацию, она действительно полная, и можно найти ответы практически на все вопросы (все остальное я быстро находил на StackOverflow):
image

Дальше по списку фичей с сайта:
  • Time travel каждый шаг выполнения теста в консоли можно нажать и вернуться на конкретное состояние всего приложения в прошлом, которое отображается прямо в браузере. И это не просто записанная картинка, а реальный DOM, можно откатиться и исследовать страницу через Chrome devtools.
  • Real time reloads как во всей модерн JS теме, поменяли исходники в тот же момент произошёл перезапуск теста в браузере (hot reload).
  • Automatic waiting многие инструкции теста асинхронные, как то переход на страницу, и Cypress сам автоматически дожидается окончания загрузки. Кроме конечно момента, когда вызовы сервера делает приложение.
  • Network traffic control Cypress может захватывать/видеть вызовы сервера и можно задать инструкцию, чтобы подождать ответ от сервера.
  • Screenshots and videos во время выполнения теста Cypress записывает видео экрана (MP4) браузера, вместе с инструкциями в окне.

Все это добро конечно же можно запускать и без открытого браузера на CI, используется headless Chrome или Electron.

Но были и некоторые проблемы.

По началу я не знал, что можно ждать окончания XHR запросов на сервер, и вставлял вместо них timeouts, и тесты рандомно падали. Поправил.
image

Потом через раз работал Hot Reload, постоянно приходилось перезапускать весь Cypress, потому что не было уверенности, что мои изменения применились. Оказалось, что в моей IDE (IntelliJ IDEA) есть такие нехорошие галочки, которые еще и включены по умолчанию, из-за которых получается что сохранение файла это не сохранение, а eventual сохранение.
image

Следующая проблема была в том, что мое приложение использовало window fetch для запросов на сервер, а Cypress видит только XHR запросы. Dirty hack из StackOverflow помог (я так понял, что метод fetch удаляется из window, чтобы браузер сделал fallback на XHR вместо fetch):
image

Далее встала проблема эмуляции мобильного браузера, просто в коде теста user agent перезаписать не получилось, но в отдельном специальном файле все получилось.
image

Далее решение CORSпроблемы:
image

Потом file upload, с наскоку не получилось, стандартные решения не сработали, зато помогла библиотека cypress-file-upload:
image

Единственная проблема которую я не смог решить это воспроизводимость теста. А именно стабильные и одинаковые начальные данные для запуска теста (fixtures), это более конфигурационная задача, а не Cypress, но все равно пока нерешенная.

В итоге, Cypress выглядит отличным инструментом для внедрения автотестирования в проект, если:
  1. Есть знание JS
  2. Нет необходимости тестировать во всех видах браузеров начиная с IE6 (на данный момент Cypress поддерживает Chrome, Chromium, Edge, Electron, Firefox). Вот обсуждение темы. Но могу сказать, что год назад, когда я начинал работать с Cypress он поддерживал только последнюю версию Chrome и Electron для запусков без UI.
  3. Хотите быстро сделать тесты и забыть о них, пока кто-то не сломает приложение :)

Cypress: берите и пользуйтесь!
Подробнее..

Приглашаем на DINS JS EVENING разбираем Chrome DevTools и Cypress

15.01.2021 16:07:39 | Автор: admin

На митапе Андрей Соколов из DINS расскажет, как разработчикам облегчить отладку с помощью Chrome DevTools. Константин Поздникин из Usetech покажет, как Cypress помогает бороться с багами в проектах со сложной бизнес-логикой. Во время прямого эфира вы сможете задать вопросы спикерам. Участие бесплатное, но нужно зарегистрироваться. Подробная программа и информация о спикерах под катом.


Программа

19:00-19:40 Инструменты отладки в Chrome DevTools (Андрей Соколов, DINS)

В Chrome DevTools есть много фишек, которые упрощают жизнь разработчиков. Андрей расскажет, как они могут сделать вашу отладку приятнее.

Вместе со спикером рассмотрим главные вкладки для отладки фронтенда: elements, console, source и network. Андрей расскажет о кнопках и функциях, которые вы видели, но не использовали.

Презентация будет полезна всем, кто взаимодействует с UI: фронтендерам, QA-инженерам, бэкенд-разработчикам.

Андрей Соколов Frontend Developer в DINS. Помимо работы увлекается написанием странных Telegram-ботов.

19:40-20:30 Cypress как ядерное оружие для багов на проекте со сложной бизнес-логикой (Константин Поздникин, Usetech)

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

В такой ситуации важно использовать интеграционное тестирование, тестирование API и unit-тесты для frontend, backend и их связки в окружении. Константин расскажет, как с этой задачей поможет справиться Cypress настоящее ядерное оружие в войне против багов. Вы узнаете, как с помощью Cypress заменить Selenium, экономить время и проводить комплексное эффективное тестирование.

Доклад будет интересен специалистам уровня Middle и выше.

Константин Поздникин ведущий разработчик компании Usetech. Преподаватель и ментор в Корпоративном университете компании.

Как присоединиться

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

Как проходят встречи

Записи предыдущих митапов можно посмотреть на нашем YouTube-канале.

О нас

DINS IT EVENING это место встречи и обмена знаниями технических специалистов по направлениям Java, DevOps, QA и JS. Несколько раз в месяц мы организуем встречи, чтобы обсудить с коллегами из разных компаний интересные кейсы и темы. Открыты для сотрудничества, если у вас есть наболевший вопрос или тема, которой хочется поделиться пишите на itevening@dins.ru!

Подробнее..

Перевод Как проводить сквозное(end-to-end) тестирование вашего приложения используя Cypress.io

04.05.2021 08:16:25 | Автор: admin
Изображение от https://unsplash.com/@kellysikkemaИзображение от https://unsplash.com/@kellysikkema

В этой статье вы узнаете:

  • Что такое Cypress и когда его стоит использовать

  • Основы тестирования с использованием Cypress

  • Расширенные команды Cypress

  • Взаимодействие с элементами пользовательского интерфейса

  • Лучшие практики с использованием Cypress


Введение

Чтобы протестировать свои приложения, вам потребуется сделать следующие шаги:

  • Запустить приложение

  • Подождать пока сервер запустится

  • Провести ручное тестирование приложения(нажать на кнопки, ввести случайные текст в поля ввода или отправить форму)

  • Проверить, что результат вашего теста корректен(изменения заголовка, части текста и т.д.)

  • Повторить эти шаги ещё раз после простых изменений кода

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

Именно здесь в игру вступает Cypress. При использовании Cypress единственное, что вам нужно сделать, это:

  • Написать код вашего теста(нажатие на кнопку, ввод текста в поля ввода и т.п.)

  • Запустить сервер

  • Запустить или перезапустить тест

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

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

Теперь, когда мы обсудили преимущества Cypress, давайте узнаем об основах этой библиотеки.


Начало

Установка и настройка Cypress

Сначала создайте отдельную папку для вашего проекта, а затем инициализируйте ее:

Инициализация проектаИнициализация проекта

Наконец, чтобы установить библиотеку Cypress:

Установка CypressУстановка Cypress

Примечание. Если вы используете дистрибутив Linux, перейдите кэтим инструкциям,прежде чем продолжить установку Cypress через NPM.

Теперь, когда Cypress установлен, попробуйте запустить его с помощью следующей команды:

Открытие CypressОткрытие Cypress

Она открывает запускалку тестов(Test Runner):

Интерфейс Test RunnerИнтерфейс Test Runner

А теперь давайте перейдём к написанию тестов.


Основы Cypress

Создание файла

Cypress требует, чтобы все наши тесты находились вкаталоге cypress/integration.Сначала перейдите в этот каталог:

Переход к cypress/integrationПереход к cypress/integration

Теперь создайте файл JavaScript с именемbasicTest.js:

Создание JavaScript файлаСоздание JavaScript файла

Если вы не отключили сервер Cypress, ваши новые файлы появятся в Test Runner в реальном времени:

Обновление структуры файлов в реальном времениОбновление структуры файлов в реальном времени

Теперь давайте напишем наш первый тест.

Простые тесты с утверждением и ожиданием значения

В вашем файле /cypress/integration/basicTest.js напишите следующий код:

Код к файлу basicTest.jsКод к файлу basicTest.js
  • Строка 1:Функция describe сообщает Cypress название набора наших тестов.

  • Строка 2: Функцияit, обозначает название теста.

  • Строка 3: Создаём утверждение.Здесь мы подтверждаем, что 2 + 2 равно 4. Если тест вернётfalse, то он будет немедленно остановлен.

Чтобы запустить вашу программу, щёлкните поbasicTest.js в вашем сервере Cypress.

Щелчок по basicTest.js в Test RunnerЩелчок по basicTest.js в Test Runner

Результат запуска:

Результат запуска тестаРезультат запуска теста

Отлично!Значит, наше утверждение было успешным.

Что, если мы сделаем заведомо ложное утверждение?Теперь в/cypress/integration/basicTest.js добавьте следующий код впределах функцииdescribe:

Код для добавление в basicTest.jsКод для добавление в basicTest.js
  • Строка 2: Если сумма 4 и 5 равна 10, тест будет пройден. В противном случае, незамедлительно остановлен.

Снова запустите код.Результат будет:

Результат нашего второго тестаРезультат нашего второго теста

Обратите внимание, как наш второй тест не удался.Если бы результат был правильным, тест прошел бы успешно.

Давайте больше поиграем с утверждениями.Добавьте в basicTest.jsследующий код:

Код для добавления в basicTest.jsКод для добавления в basicTest.js
  • Строка 2: Если сумма 5 и 5неравна 100, то тест должен пройти.

Результат выполнения теста:

Результат теста: успешно!Результат теста: успешно!

Отлично!Наш тест прошел. Функцияexpect выполняетBDD (behavior-driven) утверждения.В следующем разделе мы выполним утверждения, основанные на тестировании(test-driven assertions).

Сейчас/cypress/integration/basicTest.jsдолжен выглядеть так:

Написание утверждений основанных на тестировании(test-driven assertions) с явным использованием assert

Мы даже можем писать утверждения на основе TDD с использованиемassert.

В вашем файлеbasicTest.js напишите следующий код:

  • Строка 2: Создаём объект со свойствамиnameи age.

  • Строка 6: ФункцияisObjectподтверждает,что переменнаяpersonявляется объектом.Если результатtrue, то будет напечатаноvalue is object.В противном случае будет показано, что этот тест не прошел.

  • Строка 10: Убеждаемся, что переменнаяnameсодержитстроковое значение.

  • Строка 14: Убеждаемся, что переменнаяname неявляется целым числом.

Запустите код.Результатом будет:

Результат запуска нашего тестаРезультат запуска нашего теста

Отлично!Наш код работает.В следующем разделе мы научимся работать с сайтами через Cypress.

Сейчас нашbasicTest.jsдолжен выглядеть так:

Запуск веб-сайтов

Здесь мы попробуем запуститьDemoblaze, сайт, созданный для проведения тестов.

В своей папке/cypress/integration/ создайте файл с именемbasicCommandsTest.js.В этом файле напишите следующий код:

Код для basicCommandsTest.jsКод для basicCommandsTest.js
  • Строка 3: Используем методvisit, чтобы сообщить Cypress о переходе на веб-сайт Demoblaze.

Сохраните свой код и нажмите наbasicCommandsTest.js в меню Test Runner:

Клик по basicCommandsTest.js вTest RunnerКлик по basicCommandsTest.js вTest Runner

Результат запуска:

Отлично!Наш код работает.В следующем разделе мы более глубоко погрузимся в тестирование с помощью Cypress.

В итогеbasicCommandsTest.jsдолжен выглядеть так:


Cypress: Расширенные команды

В этом разделе мы попытаемся взаимодействовать с элементами на странице.Однако, прежде чем продолжить этот процесс, нам нужно сначала научиться идентифицировать элементы HTML в Cypress.

Как идентифицировать элементы

Cypress используетселекторы JQueryдля идентификации компонентов на веб-странице.

Например, чтобы получить элемент myButton используя id, мы должны написать следующий код:

Получение элемента через id элементаПолучение элемента через id элемента

В качестве альтернативы, чтобы идентифицировать элемент myButton используя имя класса, могли бы использовать следующую строку:

Получение элемента через имя классаПолучение элемента через имя класса

Давайте теперь поработаем с взаимодействием с пользовательским интерфейсом нашего сайта.

Нажатие кнопки

В этом разделе мы будем использоватьстраницу The-Internetдля запуска наших тестов.На этом веб-сайте мы будем использоватьразделдобавления/удаленияэлементов.

Давайте сначала попробуем идентифицировать нашу кнопку Добавить элемент.

Страница для тестированияСтраница для тестирования

Используя DevTools, заметьте, что уbuttonесть свойствоonclick, имеющее значениеaddElement().

Скриншот из DeveloperToolsСкриншот из DeveloperTools

Соответствующий селектордля этой кнопки будет выглядеть так:

Идентификация элементаИдентификация элемента

В папке/cypress/integration создайте файл с именемrunningClickCommand.js.В этом файле напишите следующий код:

  • Строка 2: Переходим на веб-страницу.

  • Строка 6: Связываем в одну цепочку получение элемента button и нажатие на эту кнопку.

Запустите код.Результат:

Вывод результатаВывод результата

Отлично, наш код работает!Обратите внимание, что как только страница загрузилась, в нашем тесте автоматически происходит нажатие на кнопкуAdd Element.

Давайте теперь поработаем с вводом текста в текстовое поле.

Ввод текста

В этом разделе мы будем использоватьстраницуThe-Internets login.Нам нужен способ сначала идентифицировать элементы.

Скриншот сайта для тестированияСкриншот сайта для тестированияСкриншот из DeveloperToolsСкриншот из DeveloperToolsСкриншот из DeveloperToolsСкриншот из DeveloperTools

Поле username имеетid равноеusername, а полеpassword имеетid равноеpassword.Кроме того, кнопка Login имеет свойствоtype равноеsubmit.Таким образом, для определения полейusername иpassword, нам понадобитсяселектор JQuery id:

Идентификация элемента через его idИдентификация элемента через его id

Более того, чтобы получить кнопкуbutton, нам понадобитсяселектор атрибутов, например:

В своей папке/cypress/integration создайте файл с именемrunningTypeCommand.js.В этом файле напишите следующий код:

  • Строка 3: Переходим на страницу входа в систему.

  • Строка 6: Переходим в полеusername и добавляем методtype вцепочку вызовов, чтобы напечатать в этом текстовом поле значениеtomsmith.

  • Строка 7: Переходим в полеpassword и вводимSuperSecretPassword.

  • Строка 10: Нажимаем на кнопку Отправить.

Запустите код.Результатом будет:

Вывод результата запуска кодаВывод результата запуска кода

И мы закончили!В следующем разделе мы узнаем о работе с чекбоксами.

Переключение чекбоксов

В этом разделе мы будем использоватьраздел чекбоксов на странице The-Internet.

Давайте сначала посмотрим на DevTools:

Developer ToolsDeveloper Tools

Оба этих чекбокса имеют свойствоtypeсо значениемcheckbox.Кроме того, они также являются дочерними элементамидля элементаform сid равнымcheckboxes.В этом случае мы бы использовалиселектор JQuery родитель-потомок:

Идентификация наших чекбоксовИдентификация наших чекбоксов

В каталоге/cypress/integration/ создайте файл с именемrunningCheckCommand.js и напишите следующий код:

  • Строка 4: Находим обе группы чекбоксов, а затем используем методcheck, чтобы отметить их выбранными.

  • Строка 7: Просим Cypress приостановить процесс тестирования на одну секунду.

  • Строка 8: Получаем список отмеченных чекбоксов.Затем используем методuncheck, чтобы снять выбор.

Запустите код.Результат:

Результат запуска тестаРезультат запуска теста

Отлично!Наш код работает.Давайте теперь поработаем над неявными утверждениями с помощью Cypress.

Неявные утверждения

Ранее мы выполняли утверждения для переменных и объектов.Однако в реальном мире мы хотели бы выполнять утверждения для текста, расположенного в нашем элементе HTML, или проверять, есть ли у нашего элементаul дочерние элементыli или нет.

Для выполнения таких утверждений мы будем использовать ключевое словоshould.В этом разделе мы будем делать неявные утверждения настраницедобавления элемента The-Internets Add Element

Скриншот тестируемой страницыСкриншот тестируемой страницыDeveloper ToolsDeveloper Tools

Наша кнопкаDelete имеет классadded-manually.Мы хотим выполнить следующее утверждение для этого элементаbutton:

Наше утверждениеНаше утверждение

Для этого мы должны использовать следующий синтаксис:

Получение элемента и за тем утверждениеПолучение элемента и за тем утверждение

Альтернативно, можем выполнить такое утверждение:

Наше утверждениеНаше утверждение

Мы можем использовать эту строку кода:

Получение элемента и затем утверждениеПолучение элемента и затем утверждение

Перейдите в/cypress/integration/runningClickCommand.jsи добавьте следующий код:

Код для runningClickCommand.jsКод для runningClickCommand.js
  • Строка 1: Получаем элементы с классомadded-manually.Затем проверяем, что их количество(have.length)равно единице.

  • Строка 3: Получаем кнопкуAdd Element, а затем проверяем, что текст на кнопке(have.text)действительно будетAdd Element.

Запустите код.Результат в конце теста должен быть следующим:

Результат запускаРезультат запуска

Отлично!Наш код работает.Теперь перейдем к изучению командыeach.

В итогеcypress/integration/runningClickCommand.js должен выглядеть так:

Команда each

Взглянитееще раз наThe-Internets Add Elements page:

Скриншот тестового сайтаСкриншот тестового сайта

Чтобы удалить все эти элементы, мы можем вручную прокликать все кнопкиDelete.Это возможно;однако рассмотрим ситуацию, когда кнопокDeleteбольше сотни.Следовательно, удаление всех их вручную займет много времени.

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

Откройте Developer Tools:

Все наши кнопкиDelete имеют свойствоclass равноеadded-manually.В этом случае мы будем использоватьселектор классови соединять его с командойeach, например:

Получение элемента и использование each затемПолучение элемента и использование each затем

Перейдите в/cypress/integration/runningClickCommand.jsи добавьте следующий фрагмент кода:

Код для runningClickCommand.jsКод для runningClickCommand.js
  • Строка 2: Получаем все элементы, у которых есть класс.added-manually.Каждый элемент в серии будет представлен параметром$el.

  • Строка 3: Обёртываем этот элемент, чтобы мы могли выполнять с ним команды Cypress.Здесь мы отправляем команду щёлкнуть по этим элементам.

Результат выполнения кода должен быть следующим:

Результат выполнения кодаРезультат выполнения кода

Наш код работает!Поскольку наш тест был быстрым, давайте попробуем добавить на страницу больше элементов.

Найдите следующий фрагмент кода:

Измените это так:

  • Строка 2: Запускаем цикл, чтобы сообщить Cypress, что нужно нажать кнопкуAdd Element 20 раз.

Запустите код еще раз.Результат должен быть таким:

Результат выполнения кодаРезультат выполнения кода

Как видите, наша программа автоматически удалила все элементы на странице без каких-либо ручных усилий.Отлично!

В следующей части статьи мы узнаем о том, что можно и чего нельзя делать при тестировании с Cypress.

В итогеcypress/integration/runningClickCommand.jsдолжен выглядеть так:


Лучшие практики

Держите тесты изолированными

Рассмотрим ситуацию, когда вы тестируете свое приложение.Структура вашего проекта будет выглядеть примерно так:

Не самая лучшая структураНе самая лучшая структура

В вашем Test Runner это будет выглядеть так:

Отображение тестовой структуры в Test RunnerОтображение тестовой структуры в Test Runner

Команда Cypress утверждает, что структура вашего проекта должна быть организована как можно лучше.Лучше всего перегруппировать ваши файлы проекта в другие папки, например:

Хорошая структура проектаХорошая структура проекта

Следовательно, это выглядело бы так:

По возможности используйте собственные команды

Взгляните на этот фрагмент кода:

Пример кодаПример кода

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

В вашемcypress/support/commands.jsнапишите этот код:

  • Строка 1: Создаём собственную команду, которая будет иметь два параметраidentifier иdata.

  • Строка 2: Получаем элемент с соответствующим идентификатором, а затем вводим в него данные.

Примечание. Считается хорошей практикой записывать свои собственные команды в файл/cypress/support/commands.js.

Теперь вернитесь в свой тестовый файл и замените его вот так:

Пример кодаПример кода

Как видите, наш код выглядит значительно короче.

Избегайте атомарных тестов

Взгляните на этот фрагмент кода:

Здесь мы повторно выполняем тесты на HTML элементе сid равнымfirst.

Cypress не одобряет такого поведения.Это неэффективно, и есть способ лучше переписать этот код, например:

Мы можем использовать методand для связывания дополнительных командshould с нашим элементом.

Не запускайте сервер в Cypress

Команда execприсутствует для запуска команд в терминале.Но запускать сервер с помощью этой команды крайне не рекомендуется.

Если вы хотите протестировать свое приложение наlocalhost, сначала запустите сервер,а затемзапустите свой тест Cypress.

Команды терминалаКоманды терминала

Репозиторий GitHub и дополнительные ресурсы

Код GitHub

Дальнейшее чтение


Заключение

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

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

Подробнее..

Cypress и его место в нашей тестовой пирамиде

18.05.2021 08:17:48 | Автор: admin

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

Введение в Cypress

Если отбросить капитанское определение, что Cypress это JavaScript-фреймворк для тестирования, то важно отметить, что при работе с ним мы видим на экране браузер. Он не обязательно открыт, он может быть headless, но он есть, и он открывает особое приложение самого Cypress, которое состоит из нескольких фреймов: в одном фрейме открывается продукт, который мы тестируем, в другом фрейме запускаются тесты. Код тестов пишется на JavaScript, поэтому они могут выполняться непосредственно в браузере ведь это нативный для него язык.

Так с помощью JavaScript API производятся все манипуляции, которые делаются в тестах, то есть заполнение форм, клики и тому подобное.

Преимущества Cypress

Нет Selenium WebDriver

Очевидное отличие Cypress от тех библиотек и фреймворков, которые мы использовали раньше это отсутствие основного действующего элемента, Selenium.

Selenium WebDriver это third-party сервис на Java, который обращается к браузеру по WebDriver протоколу. Это накладывает ограничения на работу с браузером в рамках протокола. Сетевое взаимодействие также вносит свой вклад во время выполнения тестов.

Изначально Selenium был создан не специально для тестов, а как общий инструмент автоматизации для браузера. Cypress, в отличие от него, сфокусирован на решении конкретной задачи, а именно, на создании end-to-end (е2е) тестов для интерфейса web-приложений.

Все в одном

Cypress не нужно собирать из кусочков он принес все достаточно современные "батарейки" с собой:

  • Синтаксис BDD (унаследовано из Mocha): describe(), context(), it().
    А также хуки: before(), beforeEach().
    Использовать такой DSL привычно для тех, кто уже писал юнит-тесты на JavaScript.

  • Библиотека ассертов (унаследовано из Chai). Например:
    expect(name).to.not.equal("Jane") ожидание того, что элемент не существует это не то же самое, что ожидание неудачи при проверке существования элемента. Если элемента нет, то это хорошо, это не нужно перепроверять, а нужно идти дальше.
    Такую задачу должен решать тестовый фреймворк, и этого нам очень не хватало в старой самописной библиотеке, при использовании которой многое ложится на плечи разработчика теста.

  • Перехват, отслеживание (spy) и подмена (mock) запросов браузера к бэкенду.

Development experience

Главное преимущество Cypress это отличный development experience. Написать первый тест для своего проекта (неважно, на каком языке написан сам проект) можно минут за 10. Потребуется добавить одну зависимость в package.json (npm install cypress), прочитать документацию про то, куда складывать файлы (cypress/integration/login.spec.js), и написать код в 5 строчек:

describe('Login', () => {it('should log in with credentials', () => {cy.visit('/login');cy.get('[name=login_name]').type(Cypress.env('login'));cy.get('[name=passwd]').type(Cypress.env('password'));cy.get('[name=send]').click();cy.get('.main-header').should('be.visible');});});

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

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

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

Одна из Best Practices говорит, что не нужно никогда писать таймаут типа "подождать 2 секунды". Абсолютно все таймауты должны ждать чего-то осязаемого, например, окончания Ajax-запроса. Можно подписаться на событие, которое случается в коде продукта. Например, когда нам через веб-сокет прилетает событие с бэкенда, то срабатывает определенный listener на фронтенде.

Вся документация Cypress и Best Practices находятся на одном сайте docs.cypress.io хотелось бы отдельно отметить высокое качество этой документации, а также мастер классов, которые команда разработки Cypress проводит и публикует в открытом доступе.

Кроме того, один из паттернов, от которых стоит отказаться это PageObject. Долгое время он считался обязательным, но для новых тестов он нам уже не понадобится.

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

Тестовая пирамида

Когда говорят про тестовую пирамиду, то обычно приводят в пример анти-паттерн "перевернутая пирамида" или "стаканчик мороженого". То есть на нижнем уровне в таком примере количество юнит тестов стремится к нулю. Лично мне этот случай кажется невероятным для зрелого проекта: ведь в этом случае разработчики должны были полность отказаться писать самые простейшие тесты откуда тогда взялись сложные е2е тесты?

Как бы то ни было, к нам это не относится у нас несколько тысяч PHPUnit-тестов с покрытием около 12% строк кода.

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

Получается, наше положение выглядит, как песочные часы нам не хватает среднего слоя в тестировании, где интеграционные тесты проверяют компоненты системы независимо друг от друга. Это горлышко песочных часов и хочется заполнить с помощью Cypress. При этом еще хочется что-то сделать с существующими е2е тестами, чтобы "заострить" вершину пирамиды. То есть, важный акцент здесь в том, что Cypress не является заместителем старого фреймворка: мы не хотим просто взять и переписать все тесты на Cypress иначе мы так и останемся на шарике мороженого. Цель тестов по-прежнему, проверять регрессию в продукте, но проверять на другом уровне, чтобы выполняться быстрее и получать результат раньше, а также быть легче в сопровождении.

Наш подход к написанию тестов

Проект, о котором идет речь, это контрольная панель Plesk. Она предоставляет пользователям интерфейс для управления хостингом веб сайтов. Функциональность панели доступна не только через UI, но и через API и CLI, которые используются для автоматизации.

Мы начали с того, что сделали следующие предположения:

  • Тесты на Cypress относятся чисто к UI. Мы не относим сюда тесты, у которых шаги выполняются через API или CLI.

  • Мы не проводим никакой дополнительной валидации, кроме той, что выполняется средствами UI. Например, если мы проверяем создание домена, то мы не отправляем запросы для проверки Web-сервера или DNS, мы считаем тест пройденным, если в UI появилось сообщение на зеленом фоне о том, что домен создан успешно. Такой подход избавляет нас от предварительной подготовки и написания тестовых сценариев.

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

Наш опыт работы с Cypress в сочетании с официальными рекомендациями привел нас к использованию следующего набора практик:

Сбрасывать состояние продукта

Мы сбрасываем состояние продукта до исходного перед запуском каждого набора тестов (Cypress рекомендует делать это перед запуском каждого теста, но мы используем облегченный вариант). Мы создаем дамп базы данных и восстанавливаем его перед прогоном каждого набора тестов (test suite / spec). Это занимает порядка 5 секунд.

before(cy.resetInstance);//=> test_helper --reset-instance//=> cat /var/lib/psa/dumps/snapshot.sql | mysql

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

Использовать фикстуры

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

cy.setupData(subscription).as('subscription');//=> test_helper --setup-data < {domains: [{ id: 1, name: "example.com" }]}

Такие объекты не будут выполнять полноценные пользовательские сценарии, но для тестирования UI их будет достаточно.

Использовать прямые URL

Мы не используем навигацию и попадаем в нужные места UI по прямым URL-ам. Мы вызываем свою специальную команду login, которая создает сессию, а затем переходим прямо на нужную страницу.

beforeEach(() => {cy.login();cy.visit('/admin/my-profile/');});

В старом фреймворке мы бы использовали PageObject для входа в главное меню, а затем переходили бы из него к нужному элементу. Здесь же этого не требуется, так как мы тестируем только необходимую страницу. Единственное дублирование это команда login, но это не выглядит проблемой.

Фронтенд без бэкенда

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

const lastChecked = 'Jan 29, 2021 04:42 PM';cy.intercept('POST', '/admin/home/check-for-updates', {status: 'success',lastChecked,newVersion: null,whatsNewUrl: null,}).as('checkForUpdates');cy.get('[data-name="checkForUpdates"]').click();cy.wait('@checkForUpdates');cy.get('[data-name="lastCheckedDate"]').should('contain', lastChecked);

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

Стабильность тестов

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

Дожидаться выполнения Ajax-запроса

Многие формы в нашем продукте отправляются с помощью Ajax-запросов без перехода страницы. Чтобы тест гарантированно прошел, необходимо перехватить этот запрос и дождаться его завершения. Так как в Cypress мы проверяем только то, что происходит в UI, мы дожидаемся нужного нам сообщения.

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

cy.intercept('POST', '/admin/customer/create').as('customerCreate');cy.get('[name=send]').click();cy.wait('@customerCreate');cy.get('.msg-box.msg-info').should('be.visible');

Дожидаться исчезновения индикатора загрузки

Кое-где в нашем интерфейсе фоновые операции, например, обновление списка, сопровождаются анимированным индикатором загрузки ("крутилкой"). Именно на таких страницах после окончания Ajax-запроса случается ошибка "element has been detached from the DOM" при попытке Cypress кликнуть на элементы списка. Поэтому мы добавляем после Ajax-запроса дополнительную строку, которая проверяет, что индикатор загрузки не виден.

cy.get('.ajax-loading').should('not.be.visible');

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

Ajax-запросы после окончания теста

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

До того момента, когда следующий тест сделает первый вызов "cy.visit()", предыдущая страница остается открытой и может отправлять Ajax-запросы (например, периодическое обновление), которые будут падать из-за ошибки авторизации (куки нет, сессии нет).

В качестве workaround можно переходить на пустую страницу, чтобы браузер сбрасывал все активные Ajax-запросы. Для этого добавляем в support/index.js

afterEach(() => {cy.window().then(win => {win.location.href = 'about:blank';});});

Первые результаты

За 3 человеко-месяца (3 итерации) мы получили следующие результаты:

  • 335 тестов на Cypress (разбиты на 84 спеки)

  • Пайплайн полностью выполняется за 35-40 минут, из которых сами тесты занимают 20 минут

  • Запуск пайплайна на каждый пулл-реквест в блокирующем режиме (то есть нельзя мержить без успешного прохождения тестов)

  • Уровень доверия выше 95% (то есть вероятность flaky падения ниже 5%)

  • Покрытие интерфейса 35% (ниже расскажу подробнее)

Пайплайн для запуска тестов

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

Как и для большинства наших задач, пайплайн запускается в Jenkins и хранится в Jenkinsfile вместе с кодом проекта.

Линейный пайплайн

В первом заходе получился простой линейный пайплайн.

Мы запускаем Docker-контейнер с Plesk в фоновом режиме и ждем, когда он будет доступен в локальной сети. Потом запускаем другой контейнер с Cypress и кодом тестов, он подключается к Plesk и выполняет все тесты, а мы ждем его завершения (не делаем detach).

Мы запускали тесты на машине с 12 ядрами, которая используется у нас для сборки Plesk и ряда его служб. В течении рабочего дня у нас бывает до 20-30 сборок. В результате Load Average достигал 20, и многие соседние процессы "вставали". Мы добавили ограничение на количество исполняемых сборок до 3-5. Но и этого оказалось недостаточно, соседи по железу продолжали жаловаться на нагрузку.

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

Пайплайн с параллельными шагами

Чтобы как-то ускорить процесс, мы решили воспользоваться Jenkins EC2 Fleet plugin, который предоставляет Jenkins slave ноду по требованию из Autoscaling Group в AWS и уничтожает неактивные ноды после некоторого простоя. Такой подход позволяет тратить деньги на аренду ресурсов только тогда, когда они необходимы.

Переход на spot-инстансы позволил нам существенно сэкономить: вместо $150 в месяц за ondemand c5.xlarge, мы стали тратить около $60 за c5.xlarge и более мощные c5.2xlarge.

А главное, мы можем делать столько одновременных запусков, сколько нам нужно.

Разворачивание новой ноды занимает порядка 2 минут. Мы сделали в пайплайне несколько шагов параллельными, чтобы за это время успеть собрать продукт и быть готовыми к его установке в Docker на новой ноде.

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

Пайплайн с параллельными тестами

В Cypress есть платная фича параллельный запуск тестов с помощью Cypress Dashboard. Но мы пошли простым и бесплатным путем перечисляем файлы с тестами при запуске контейнера, при этом первый запускает все четные файлы, второй все нечетные.

cypress run --spec $(find 'cypress/integration' -type f -name '*.js' | awk '(NR - ${RUNNER}) % ${TOTAL_RUNNERS} == 0' | tr '\n' ',')

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

В итоге мы укладываемся в приемлемые 35-40 минут для всего пайплайна, а время одной пачки тестов занимает примерно 20 минут.

В дальнейшем с увеличением количества тестов нам, очевидно, потребуется больше параллельных потоков, и мы планируем использовать Amazon ECS для запуска контейнеров, чтобы не ограничиваться EC2 инстансом.

Измерение URL coverage

В нашем проекте используется много различных языков программирования и анализ code coverage является больной темой, потому что сбор данных требует специальных билдов и объединения отчетов с нескольких конфигураций.

Для анализа тестового покрытия UI мы решили воспользоваться продуктовой аналитикой и сравнить данные, полученные от тестовых инсталляций, с данными от реальных пользователей. У нас уже был сервис, аналогичный Google Analytics, для сбора пользовательских метрик, а тестовые данные складывались отдельно и никем не использовались. Из множества метрик мы отфильтровали события о посещенных URL-ах (страницах) продукта, начали сохранять эти данные в удобном для нас виде в базу данных и составлять отчет по посещенным адресам.

По полученным данным, за счет всего автоматического и ручного тестирования внутри компании мы покрываем около 60% URL-ов, которые посещают реальные пользователи в течении месяца. Наши старые тесты покрывают около 25%, а новые тесты на Cypress уже достигли 35%.

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

Следующие шаги

Ускорить сборку Docker

Одна из проблем, над которой мы хотим поработать ускорение сборки контейнеров Docker. Как уже было сказано выше, мы создаем временный сервер в AWS (slave node) для каждой сборки Docker, и эта сборка на данный момент занимает в среднем 8 минут. Но поскольку каждый временный сервер новый, то мы совершенно не используем преимущества кэширования, а хотелось бы ими воспользоваться. Поэтому сейчас мы исследуем возможность использования BuildKit. Альтернативными решениями могут стать Kaniko или AWS CodeBuild.

Сократить количество е2е тестов

Мы хотим уменьшить количество старых е2е тестов, чтобы вершина тестовой пирамиды стала более острой. Сейчас около трети этих тестов относятся к UI. Кроме этого, тесты содержат проверки через командную строку (CLI), причем иногда логика тестов в UI и CLI дублируется.

Основная идея: перенести все UI-тесты в Cypress, а в старом фреймворке оставить только CLI-тесты с детальными проверками. Поэтому для каждого UI-теста из старого фреймворка мы делаем следующее:

  1. Заменяем UI-шаги на CLI (если это возможно).

  2. Удаляем, если уже есть аналогичный тест с CLI.

  3. Если проверка возможна только через UI уносим ее в Cypress.

Например, при создании домена проверяется то, что он резолвится, и что на нем работают определенные скрипты. Эти проверки останутся только для создания домена через CLI. А тест на UI в Cypress будет проверять только появление сообщения о создании домена.

В результате мы избавимся от дублирования тестовых сценариев, сократим нагрузку на сервера с Selenium и в перспективе совсем от них избавимся, когда тестирование UI будет делать только Cypress.

Заключение

Cypress подкупает быстрым стартом к написанию тестов и удобными средствами для отладки. При этом не стоит забывать, что скорость и стабильность тестов зависят от того, как они написаны: легковесные фикстуры и подготовленные ответы бэкенда дают большое ускорение, а сброс состояния позволяет избежать случайного влияния тестов друг на друга.

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

Кроме того, стоит отметить, что запуск тестов потребляет довольно много ресурсов CPU, поэтому стоит заранее оценить количество параллельных запусков и спланировать масштабирование инфраструктуры.

Подробнее..

Портативный прибор для измерения влажности воздуха

18.03.2021 02:18:25 | Автор: admin

У меня есть друг, который говорит, что если посчитать количество часов потраченное на изготовление этого прибора, то дешевле купить самый дорогой. Я конечно с ним не согласен, но не буду долго останавливаться на этой провокационной теме. Скажу лишь, что по времени, разработка и изготовление этого прибора заняла около 30 часов. Я использовал детали которые были у меня в наличии, в том числе датчик влажности, был куплен только аккумулятор. Для меня подобные проекты нужны ещё за тем, чтобы прокачать навыки, узнать что то новое, и получить прибор на свой вкус.

Прибор нужен был автономный, с временем автономной работы около 12 часов. Соответственно схема питания USB-> контроллер заряда -> повышающий преобразователь до +5 В. Питание выбрал 5 В, так как датчик влажности, имеющийся в наличии (HIH-4010-004), калиброван при питании 5 В. При покупке к датчику прилагалась распечатка, со всеми константами для расчета RH% по выходному напряжению, для достижения точности измерения влажности 3.5%.

Тема датчиков влажности - это отдельная тема.Если коротко, оказалось что они очень нежные. На HIH-4010-004 в datasheet есть запись, что долгая эксплуатация при влажности более 90% может привести к сдвигу показаний на 3% . А вот для более новых датчиков HDC2080 от TI указано, что рекомендуемый диапазон влажности для работы, от 20% до 80% при температуре воздуха от 0 до 60. Если долго эксплуатировать датчик за пределами этого диапазона, то используемый в датчике полимер будет деградировать, и показания будут сдвигаться с долгим временем восстановления.

В 2014 году Cypress устроил промо, на микроконтроллеры PSOC 4200 можно было купить за $1 с бесплатной доставкой, в том числе и в Россию. И я не устоял и прикупил пару десятков в QFP корпусе и десяток в SSOP.

Микроконтроллеры примечательны тем, что помимо Cortex-M0, 32KB EEPROM и 4K RAM. У них на борту присутствовало четыре UDB блока (вариант программируемой логики), в каждом из которых было два PLD (Programmable Logic Device) и 8 битное ALU с возможностью выполнять до 8 команд в цикле, работающий на тактовой частоте процессора 48 МГц. Так как в своих прошлых проектах часто надо было городить мелкую логику вокруг контроллера, тема UDB меня привлекла.

Также на борту микроконтроллера был необычный контроллер LCD, он позволял управлять мультиплексными LCD индикаторами, не как обычно, а используя для формирования напряжения смещения резистивные делители, а используя ШИМ, а для LCD с количеством разрядов до 4 - Цифровую Корреляцию.

Как оказалось изображение получается достаточно контрастным, в данном приборе применил регулировку контрастности в зависимости от температуры. Всего 3 ступени, меньше 0, от 0 до 30 и больше 30.

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

По программе, работа с датчиком DS18B20 организована через UART в прерывании, поэтому пришлось тактовую частоту микроконтроллера сделать 24 МГц, чтобы получить целые делители для скоростей 9600 и 115200. Разница в потреблении на 6 МГц и 24 МГц - 4 мА. Наверное можно было как то выкрутиться и сделать минимальную тактовую частоту, но я не стал возиться, так как для данного прибора не предполагалось долгой автономной работы.

Преобразование измеренного напряжения датчика влажности в RH% сделано в целых числах, с учетом напряжения питания и температурной компенсацией. В моем случае напряжение на выходе повышающего преобразователя оказалось 5.16 В. По этой причине, и по причине того, что у каждого датчика HIH-4010-004 свои калибровочные константы, прошивка для микроконтроллера не Git не размещена. Так как среда разработки PSOC Creator абсолютно бесплатная, а скомпилировать прошивку можно с помощью GCC компилятора, это не должно стать проблемой.

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

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

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

При длительном нажатии единственной кнопки или спустя 60 минут после включения, микроконтроллер уходит в режим STOP . Микроконтроллер потребляет в режиме STOP всего 80 нА. Так как в режиме STOP у микроконтроллера выходы замораживаются , контроллер LCD заранее отключаем, а на выводе ENABLE импульсного стабилизатора устанавливаем 0, тем самым отключаем стабилизатор и датчики.

И немного техно обнаженки.

Сразу хочу сказать, что я чистюля, и мою платы в изопропиловом спирте по нескольку раз. Но с этой платой случай особый, во-первых стекляшка LCD не любит растворители, во вторых начитался про датчик влажности HDC2080, что не в коем случае, ни в чем, нельзя отмывать плату после пайки. Только безотмывочные флюсы и паяльные пасты. Поэтому наблюдаются остатки флюса на плате после отладки. Один проводок на обратной стороне - это косяк в разводке платы (косяк устранен). Второй был припаян на момент отладки без датчика DS18B20, так как ножки датчика служили соединителями дорожек между слоями, да так и был оставлен.

Корпус датчика нарисован в Компас 3D и распечатан на 3D принтере. До сих пор не придумаю как сделать нормальные защелки для соединения двух половинок корпуса, поэтому просто засверливаю с торцов и креплю маленькими шурупами.

Сверловка платы на CNC станке, программы для сверловки и фрезеровки сделаны в FlatCam из Gerber файлов сгенерированных Eagle. Фотошаблон напечатан на лазерном принтере, выдержан в парах ацетона 20 минут, после чего стал достаточно чёрным для изготовления печатной платы с помощью фоторезиста.

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

<a href="http://personeltest.ru/aways/github.com/Aleksei521/humidity-measure">Файлы вместе с исходниками на GitHub.</a>
Подробнее..

QA Online Meetup 2810

23.10.2020 16:09:30 | Автор: admin
Приглашаем на открытый QA meetup, он пройдет 28 октября. Поговорим об инструменте Cypress и посмотрим его в действии, а также обсудим роль оркестраторов бизнес-логики в корпоративных приложениях.

Подключайтесь к нам!



О чем будем говорить


Как мы начали использовать Cypress в тестировании и не прогадали

Марина Парусникова, Райффайзенбанк

О спикере: старший тестировщик в Райффайзенбанке. Имею опыт в тестировании более 6 лет. В начале карьеры занималась ручным тестированием десктопного приложения, написанием юнит-тестов, разработкой эмулятора. В Райффайзенбанке погрузилась в мир автоматизации тестирования веб-приложений, испробовала BDD и TDD на практике, занималась нагрузочным тестированием. Люблю выстраивать удобные процессы в команде, считаю особенно ценным достижение синергии в работе тестировщиков и разработчиков.

О докладе: Cypress набирающий известность фреймворк для автоматизации тестирования веб-приложений. Можно ли им полностью заменить автоматизацию на Selenium+Cucumber? Как начать тестировщикам использовать Cypress для автоматизации тестов? Поделюсь опытом в выступлении.


Оркестр на изоляции: автоматизируем тесты Camunda

Денис Кудряшов, Леруа Мерлен

О спикере: Прошёл стандартный карьерный путь (из тестирования в разработку) наоборот. Ушел из разработки в тестирование более 3-х лет назад, сейчас занимаюсь автоматизацией. Как-то раз тестировал сайт и случайно нашел уязвимость в Яндексе.

О докладе: В докладе речь пойдёт о роли оркестраторов бизнес-логики в корпоративных приложениях. Я расскажу о том, какие системы и каким образом используются в Леруа Мерлен. Мы исследуем систему управления бизнес-процессами Camunda с точки зрения тестирования реализаций BPMN схем, а также поговорим об инфраструктуре тестовых окружений (как мы понимаем и поднимаем их у себя в Леруа). И всё это с примерами!

>> Начнём митап в 18:00 (МСК).
Регистрируйтесь, чтобы получить ссылку на трансляцию: письмо со ссылкой придет вам на почту. Мы вас ждем, до встречи online!
Подробнее..

QA Online Meetup 2411

19.11.2020 18:06:36 | Автор: admin
Присоединяйтесь на второй открытый митап 24 ноября, который посвятим интеграционному тестированию. Приготовили отличные доклады, и вот о чем поговорим: зачем и как использовать Cypress для интеграционного тестирования, и возможно ли добиться нуля ошибок по таким тестам?

Ждем вас онлайн!



О чем будем говорить


Cypress для интеграционного тестирования. Зачем? Как?

Светлана Голдобина, Райффайзенбанк

О спикере: Старший тестировщик в Райффайзенбанке, команда Cash Management. Опыт в автоматизации тестирования больше 3-х лет в крупных банках. За карьерный путь успела поработать как с BDD фреймворками, в том числе Akita, и поучаствовать в ее развитии, так и перейти полностью на сторону тестирования на стеке разработчиков JavaScript/TypeScript, Java + Spring.

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

О докладе: В нашей команде писали тесты в BDD стиле на русском языке (Selenium/Selenide + Cucumber + Java). Казалось бы, куда еще проще и прозрачнее для команды? Однако, как только мы лишились нескольких QA, и разработчикам пришлось писать и дорабатывать тесты, наш инструмент стал стоппером в тестировании, и BDD тут ничем не помог. В докладе расскажу, как мы опустили тестирование на дно и начали его восстанавливать.

Как мы добились нуля ошибок по итогам интеграционных тестов

Максим Плавченок, Bercut

О спикере: Работаю в компании Bercut с 2002. Начинал карьеру в телекоме: прошёл путь от сменного инженера до руководителя направления интеграционного тестирования. Был разработчиком, тестировщиком, менеджером продукта. Люблю заниматься тем, что приносит проблемы (и решать их), поэтому и пришёл в тестирование.

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

У нас ушло на это 2 года, и это получилось за счёт улучшений по нескольким направлениям: инструменты, экспертиза, документация, работа с заказчиком, команда. Улучшения различались по значимости: знать специфику и процессы заказчика важно, перейти к новой шкале оценки сложности задач опционально, а работать с мотивацией команды критически важно.

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

>>> Начнем митап в 18:00 (МСК).
Зарегистрируйтесь, чтобы получить ссылку: письмо с трансляцией придет на почту всем участникам.

До встречи онлайн!
Подробнее..

Считаем code сoverage с cypress

22.12.2020 12:11:10 | Автор: admin

Привет всем, хочу рассказать как добавить code coverage к angular/react проекту. В сети можно отыскать довольно много вариантов как это делать, и со своего опыта должен заметить что иногда с angular это не так просто. Рассмотрим как добавить code coverage к 11 версии angular (если у вас 7/8 этот пример может не работать, лучше обновится)

Angular

Установим всезависимости

npm i -D ngx-build-plusnpm i -D istanbul-instrumenter-loadernpm install -D @cypress/code-coverage

Переедемс protractor на cypress

ng add @briebug/cypress-schematic

Создадим файл cypress/coverage.webpack.js

module.exports = {  module: {    rules: [      {        test: /\.(js|ts)$/,        loader: 'istanbul-instrumenter-loader',        options: { esModules: true },        enforce: 'post',        include: require('path').join(__dirname, '..', 'src'),        exclude: [          /\.(e2e|spec)\.ts$/,          /node_modules/,          /(ngfactory|ngstyle)\.js/        ]      }    ]  }};

Обновим наш angular.jso

"serve": {  "builder": "ngx-build-plus:dev-server",  "options": {    "browserTarget": "angular-registration-login-example:build",    "extraWebpackConfig": "./cypress/coverage.webpack.js"  }},

Добавим к cypress/support/index.js

import '@cypress/code-coverage/support';

Добавим к cypress/plugins/index.js

module.exports = (on, config) => {  require('@cypress/code-coverage/task')(on, config)  // IMPORTANT to return the config object  // with the any changed environment variables  return config}

Запустим приложение, в console должна быть переменная window.__coverage__

После запуска приложения остается только запустить тесты npm run cy:open / cy:run

"cy:open": "cypress open","cy:run": "cypress run",

Как только тесты пройдут в корне приложения будет создана папка coverage

Посмотреть результат можно открыв файл coverage/lcov-report/index.html

Кликая по компонентам можно посмотреть какой код выполнялся и сколько раз, а какой еще не покрыт тестами

Сампроектможно скачать тут: https://github.com/NikolayKozub/angular-coverage-cypress

React

Установим все зависимости

npm i -D nycbabel-plugin-istanbulistanbul-lib-coverage@cypress/code-coverage@cypress/instrument-cra

Добавим к cypress/plugins/index.js

module.exports = (on, config) => {  require('@cypress/code-coverage/task')(on, config)    return config}

Добавим к cypress/support/index.js

import '@cypress/code-coverage/support'

Добавим скрипты к package.json

"start": "react-scripts start","start:coverage": "react-scripts -r @cypress/instrument-cra start",

Добавим .babelrc в корень проекта

{  "plugins": ["istanbul"]}

Запускаем тесты и смотрим отчет

Еще больше можно почитать тут

Подробнее..
Категории: Reactjs , Angular , Cypress , Code coverage

Перевод Cypress VC Selenium

17.06.2021 18:06:38 | Автор: admin

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

Вот вам вопрос на миллион долларов: является ли Cypress чем-то большим, чем платформа для автоматизации веб-тестов и может ли он заменить Selenium?

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

Краткое описание

Cypress - это тестовая веб-платформа нового поколения. Она была разработана на основе Mocha и Chai и представляет собой платформу для сквозного тестирования на основе JavaScript.

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

Архитектура

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

В Selenium, когда мы запускаем сценарий автоматизации Selenium, клиентская библиотека Selenium взаимодействует с Selenium API, который отправляет команду привязки драйверу браузера с помощью проводного протокола JSON. Драйвер браузера использует реестр HTTP для фильтрации всех команд управления HTTP-запроса и HTTP-сервера. Затем команды браузера выполняются в скрипте selenium, а HTTP-сервер отвечает скрипту автоматизированного тестирования.

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

Установка

В Cypress нет никакой предварительной настройки, просто установите файл.exe и автоматически настройте все драйверы и зависимости. Автоматизация может быть выполнена за считанные минуты. Одной из философий дизайна Cypress было сделать весь процесс тестирования удобным и комфортным для разработчиков, упаковки и объединения.

Чтобы использовать Selenium в проекте автоматизации, необходимо установить библиотеки линковки языков для использования предпочитаемого языка. Кроме того, в отношении браузеров, для которых вы хотите автоматически проводить проверки, вам понадобятся двоичные файлы для WebDriver.

Если мы также примем во внимание время и сложность имплементации, здесь Cypress имеет преимущество над Selenium.

Поддерживаемые языки

Cypress поддерживает только JavaScript. Поддержка других языков отсутствует, что иногда вынуждает пользователя изучать определенный язык сценариев.

Selenium, в свою очередь, поддерживает широкий спектр языков Java, C#, Python, Ruby, R, Dar, Objective-C, Haskell и PHP, а также JavaScript.

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

Кросс-браузерная поддержка

Cypress поддерживает Canary, Chrome, Chromium, Edge, Edge Beta, Edge Canary, Edge Dev, Electron, Firefox (бета-поддержка), Firefox Developer Edition (бета-поддержка), Firefox Nightly (бета-поддержка).

Selenium поддерживает почти все основные браузеры на рынке, что является дополнительным преимуществом Selenium. Ниже приведен список поддерживаемых браузеров: Chrome (все версии), Firefox (54 и новее), Internet Explorer (6 и новее), Opera (10.5 и новее), Safari (10 и новее).

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

Параллельное исполнение пакета автоматизированного тестирования

По сравнению с Selenium в параллельной проверке, cypress отстает.

Selenium имеет много вариантов для параллельного исполнения, что для автоматизации тестирования очень важно. Grid широко используется в сообществе QA с TestNG для параллельного исполнения. А контейнеризация Docker может быть быстро интегрирована.

Производительность

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

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

Интеграция процессов автоматизации с CI/CD

Cypress: Возможна, но ограничена. Существует только одна альтернатива для командной строки и библиотеки npm - Mocha. Служба CI должна поддерживать npm, а запись тестов для большинства записей на сервере CI будет платной.

Selenium: Возможно применение CI/CD. Любая библиотека тестов, их запись и шаблоны исполнения могут использоваться и быстро адаптироваться к требованиям.

Лицензирование

Cypress также доступен под лицензией MIT как open-source. Однако, если сравнивать его с Selenium, все функции Cypress не будут бесплатными, например, приборная панель Cypress бесплатна для seed и платна для Sprout, Tree и Forest. (https://www.cypress.io)

Selenium разрешен под лицензией Apache 2.0, правообладатель Software Freedom Conservation.

Поддержка ОС

Cypress: Windows, Mac, Linux

Selenium: Windows, Linux, Mac, Android, iOS

Поддержка BDD и DataDrivenTesting

Selenium поддерживает BDD и data-driven с помощью внешних библиотек, что в Cypress невозможно.

Локаторы для идентификации объектов

Cypress поддерживает только CSS и Xpath.

Cypress поддерживает все виды веб-локаторов, такие как ID, Name, XPath, селекторы CSS, текстовые ссылки, текстовые частичные ссылки и т.д.

Отчет о выполнении

Selenium: Extent, Allure и любые другие дашборды могут быть реализованы в наборах автоматизации.

Cypress: Дашборд - это как раз Cypress.

Окончательный вердикт

Selenium больше ориентирован на специалистов по автоматизации тестирования, а Cypress - на разработчиков для повышения эффективности TDD. Selenium был представлен в 2004 году, поэтому у него больше поддержки экосистемы, чем у Cypress, который был разработан в 2015 году и продолжает расширяться. Когда мы используем Selenium, в браузере можно манипулировать несколькими различными опциями, такими как Cookies, Local Save, Screen, Sizes, Extensions, Cypress control line options, но опция сети может быть обработана только при использовании Cypress.

Однако, вот некоторые из преимуществ, о которых заявляет Cypress:

1. Программа будет делать скриншоты во время проведения экспериментов. Затем мы можем просмотреть каждый запрос в качестве разработчика теста на панели Test Runner, чтобы увидеть, что произошло на каждом этапе.

2. Для тестирования с помощью Cypress не требуется никакого ожидания или режима сна. Прежде чем продолжить, он немедленно ожидает команд и утверждений.

3. Подобно модульным тестовым ситуациям, "шпионы" Cypress могут подтверждать и проверять действия функций, ответы сервера или таймеры по времени. С помощью Cypress вы можете заглушить сетевой трафик и персонализировать вызовы API в соответствии с вашими потребностями.

Надеемся, что приведенные выше пункты помогут определить, какой метод вам лучше использовать и изучить.


В преддверии старта курса "Java QA Engineer. Professional" приглашаем всех желающих на бесплатный двухдневный интенсив, в рамках которого мы рассмотрим CI- и CD- процессы, изучим основные инструменты и ключевые понятия (Server, agents, jobs. Fail fast, Scheduling, WebHooks). Подробно познакомимся с программной системой Jenkins и научимся интегрировать ее с git и Docker.

Записаться на интенсив:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru