Конечное тестирование дает нам много преимуществ, когда речь идет о тестировании программного обеспечения. Добавив даже один тест, мы можем охватить достаточно большую территорию с точки зрения проверки функциональности приложения с точки зрения конечного пользователя. Вот как я настроил Cypress в проекте TypeScript и развернул его в Netlify, воспользовавшись преимуществами CI.
Начало работы
В этой статье я предположу, что у вас есть проект, в котором уже настроены как минимум TypeScript и ESLint. Давайте сразу перейдем к тому, как добавить Cypress!
Добавление зависимостей
Начнем с добавления Cypress и Cypress Testing Library в список зависимостей.
yarn add -D cypress @testing-library/cypress
Библиотека тестирования Cypress дает нам дополнительные команды, findBy
, findAllBy
, queryBy
и queryAllBy
, с которыми работать еще приятнее. Это необязательно, но рекомендуется. Подробности смотрите в документации.
Настройка Cypress
Чтобы настроить Cypress так, чтобы мы могли писать тесты на TypeScript, нам понадобятся следующие файлы: cypress.config.ts
, cypress/support/e2e.ts
, cypress/support/commands.ts
, и cypress/tsconfig.json
.
Давайте рассмотрим каждый из них.
// cypress.config.ts
import { defineConfig } from "cypress"
export default defineConfig({
e2e: {
baseUrl: "http://localhost:8000",
},
})
Обратите внимание, что baseUrl будет тем, который вы используете для вашего сервера разработки. Это может быть http://localhost:3000
или http://localhost:8080
или что-то подобное.
Далее добавьте еще два конфигурационных файла, чтобы импортировать наши команды и указать компилятору TypeScript использовать глобальные типы Cypress в наших тестах.
// cypress/support/e2e.ts
import "./commands"
import "cypress-axe"
import "@testing-library/cypress/add-commands"
// cypress/support/commands.ts
/// <reference types="cypress" />
/// <reference types="@testing-library/cypress" />
👌 Все дело в завершении кода!
И, наконец, рекомендуемый tsconfig.json
выглядит следующим образом:
// cypress/tsconfig.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "@testing-library/cypress"],
"noEmit": true
},
"include": ["./**/*.ts"]
}
Настроить ESLint
Если мы не настроим ESLint, то получим кучу красных загогулин, так что давайте сделаем наш линтер счастливым, а нашу жизнь проще.
yarn add -D eslint-plugin-cypress
И добавьте следующий файл в каталог cypress
:
// cypress/.eslintrc.json
{
"plugins": ["cypress"]
}
Вот и все!
Пишем тесты
Теперь начинается настоящее веселье! Давайте напишем наш первый тест для проверки доступности наших страниц. Для этого нам нужно добавить зависимость для использования API веб-тестирования axe-core.
yarn add -D cypress-axe axe-core
Утверждается, что с помощью этой библиотеки мы можем автоматически найти в среднем 57% проблем WCAG. Кроме того, этот инструмент направит наше внимание на ряд ручных проверок, на которые нам следует обратить более пристальное внимание.
В настоящее время это мой любимый инструмент автоматизации доступности, который может помочь вам писать более доступный и качественный код. Для получения дополнительной информации обратитесь к документации.
И не забудьте также обновить конфигурацию TypeScript:
// cypress/tsconfig.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "@testing-library/cypress", "cypress-axe"], // highlight-line
"noEmit": true
},
"include": ["./**/*.ts"]
}
Теперь давайте напишем первый тест!
// cypress/e2e/accessibility.cy.ts
describe("Accessibility tests", () => {
it("Has no detectable accessibility violations on load", () => {
cy.visit("/").get("main").injectAxe()
cy.checkA11y(null, {
includedImpacts: ["critical", "serious", "moderate"],
})
})
})
Итак, что же здесь происходит? Ну, у нас есть один тест, загружающий главную страницу, просматривающий main
и выполняющий наши тесты доступности. Мы рассматриваем «критические», «серьезные» и «умеренные» нарушения, которые вы можете изменить в соответствии со своими потребностями.
Теперь давайте немного расширим это, чтобы загрузить главную страницу и, допустим, конкретную запись в блоге, которую пользователь может открыть с этой страницы:
// cypress/e2e/accessibility.cy.ts
const axeRunContext = {
exclude: [[".prism-code"]],
}
const axeRunOptions = {
includedImpacts: ["critical", "serious", "moderate"],
}
describe("Accessibility tests", () => {
beforeEach(() => {
cy.visit("/").get("main").injectAxe()
})
it("Has no detectable accessibility violations on load", () => {
cy.checkA11y(null, axeRunOptions)
})
it("Navigates to blog post and checks for accessibility violations", () => {
cy.findByText(/making a create react app template/i)
.click()
.checkA11y(axeRunContext, axeRunOptions)
})
})
Мы можем исключить определенные селекторы CSS, и в данном случае мы исключаем .prism-code
. Многократная проверка блоков кода может быть дорогостоящей. В идеале мы можем один раз проверить их в отдельных тестах и пропустить во всех остальных. Это удобный способ сделать это.
Запуск тестов локально
Когда Cypress установлен, настроен и написан наш первый тест, мы готовы начать вечеринку.
Вы можете запустить тесты следующим образом:
yarn run cypress open
🥳 Ура! Мы проводим сквозное тестирование всех вещей!
Если вы впервые используете Cypress, вы можете влюбиться в него, как и я. Он делает тестирование намного проще и с ним приятно работать по сравнению с альтернативами, которые я рассматривал.
Давайте рассмотрим некоторые способы улучшения работы.
Добавьте скрипты npm
Теперь добавим несколько скриптов в package.json
, чтобы облегчить нашу жизнь:
"scripts": {
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test develop http://localhost:8000 cy:open",
"test:e2e:ci": "start-server-and-test develop http://localhost:8000 cy:run",
}
Нам также нужно добавить start-server-and-test
, чтобы мы могли запускать наши тесты, не беспокоясь о том, запущен ли сервер разработки в другом терминале.
yarn add -D start-server-and-test
И еще одна деталь, не забудьте добавить cypress/videos
и cypress/screenshots
в ваш .gitignore
, поскольку нам не нужно будет отслеживать их в системе контроля версий.
Для локального запуска наших тестов мы будем использовать:
yarn test:e2e
И вы заметите, что мы также можем запустить Cypress без головы, что полезно при запуске из командной строки и использовании с Continuous Integration (CI):
yarn test:e2e:ci
Непрерывная интеграция
Этот последний шаг будет выглядеть по-разному в зависимости от того, какой CI вы используете и как он у вас настроен.
Однако мы пытаемся добиться того, чтобы код, который мы отправляем в продакшн, всегда был проходящим. Другими словами, мы определяем наши требования в тестах. Поскольку наша логика со временем меняется, мы можем быть гораздо более уверены в том, что случайно не отправим в продакшн что-то сломанное, то есть не отвечающее этим требованиям.
В идеале, это будет сопровождаться как минимум двумя мерами предосторожности: тестированием перед развертыванием в CI и тестированием на GitHub перед разрешением слияния.
В этом примере я покажу вам, как это сделать с помощью Netlify, которая имеет хорошо продуманную и удобную для разработчиков платформу CI.
Настройка Netlify
Во-первых, нам понадобится netlify-plugin-cypress
.
yarn add -D netlify-plugin-cypress
Самая базовая рекомендуемая настройка выглядит следующим образом:
# explicit commands for building the site
# and the folder to publish
[build]
command = "yarn build"
publish = "public"
[build.environment]
# cache Cypress binary in local "node_modules" folder
# so Netlify caches it
CYPRESS_CACHE_FOLDER = "./node_modules/CypressBinary"
# set TERM variable for terminal output
TERM = "xterm"
[[plugins]]
# runs Cypress tests against the deployed URL
package = "netlify-plugin-cypress"
Однако это приводит к определенной проблеме: по умолчанию Netlify запускает тесты Cypress после успешной сборки и развертывания. У нас может быть вполне работоспособная сборка с технической точки зрения, но с потенциально неудачными тестами, и мы не хотим, чтобы это попало в производство.
С помощью нескольких дополнительных строк мы можем потребовать тесты в два других момента: (1) до сборки (preBuild
) и (2) после сборки (postBuild
), но до развертывания (onSuccess
).
Я рекомендую вам выбрать один из первых двух вариантов или оба, чтобы запустить все ваши тесты, а затем выполнить быструю проверку на вменяемость после развертывания. Такой вид тестирования часто называют дымовым.
Я выбрал запуск тестов перед сборкой следующим образом:
# netlify.toml
[build]
command = "yarn build"
publish = "public"
[build.environment]
CYPRESS_CACHE_FOLDER = "./node_modules/CypressBinary"
TERM = "xterm"
[[plugins]]
package = "netlify-plugin-cypress"
[plugins.inputs.preBuild]
enable = true
start = 'yarn start'
wait-on = 'http://localhost:8000'
wait-on-timeout = '30'
Хорошо, отлично, теперь, если код каким-то образом будет отправлен на развертывание, а тесты не пройдут, развертывание будет остановлено. Это то, чего мы хотим.
Но теперь мы запускаем все наши тесты дважды, что может стать довольно долгим и вычислительно затратным по мере роста нашего приложения. Давайте сделаем дымовые тесты, о которых я только что говорил, и проведем быструю проверку на вменяемость, чтобы убедиться, что все в порядке.
Добавим дымовой тест
Давайте добавим еще один тест:
// cypress/e2e/smoke.cy.ts
describe("Smoke test", () => {
it("should visit the index page and particular blog post", () => {
cy.visit("/")
.get("main")
.should("exist")
.findByText(/making a create react app template/i)
.click()
.get("main")
.should("exist")
})
})
Как я уже сказал, просто быстрая проверка, чтобы убедиться, что индексная страница загружается, и мы можем нажать на определенную ссылку, и эта страница также загрузится. Если это не удается, значит, что-то действительно не так.
Давайте снова настроим netlify.toml
:
# netlify.toml
[build]
command = "yarn build"
publish = "public"
[build.environment]
CYPRESS_CACHE_FOLDER = "./node_modules/CypressBinary"
TERM = "xterm"
[[plugins]]
package = "netlify-plugin-cypress"
[plugins.inputs] // highlight-line
spec = "cypress/e2e/smoke.cy.ts" // highlight-line
[plugins.inputs.preBuild]
enable = true
start = 'yarn start'
wait-on = 'http://localhost:8000'
wait-on-timeout = '30'
Теперь тесты будут запускаться до сборки (preBuild
), и только дымовой тест будет запущен после развертывания (onSuccess
).
Но подождите, есть еще кое-что.
Пока все хорошо, но дальше будет только лучше.
🍰 Как насчет того, чтобы добавить немного глазури на этот торт?
Вы знаете, что при локальной разработке вы получаете скриншоты и видео с тестами? Насколько я знаю, в Netlify CI нет возможности получить эти материалы без дополнительных усилий.
Если вы еще не сделали этого, перейдите на cypress.io и создайте учетную запись. Если вы хотите остаться на бесплатном уровне, вам придется сделать все публичным, что меня вполне устраивает.
Теперь вы можете наблюдать за своими неудачами на публике 😂. Это закаляет характер.
Но на самом деле, у меня было несколько крайних случаев, когда локально все проходило, а в CI все проваливалось, что, скорее всего, давало повод еще раз взглянуть на ситуацию. Итак, нам действительно нужно время от времени смотреть на эти активы, и Cypress Dashboard — это решение!
Для этого нам нужно сделать еще пару вещей.
Во-первых, давайте еще раз настроим netlify.toml
:
# netlify.toml
[build]
command = "yarn build"
publish = "public"
[build.environment]
CYPRESS_CACHE_FOLDER = "./node_modules/CypressBinary"
TERM = "xterm"
[[plugins]]
package = "netlify-plugin-cypress"
[plugins.inputs]
record = true // highlight-line
spec = "cypress/e2e/smoke.cy.ts"
[plugins.inputs.preBuild]
enable = true
start = 'yarn start'
wait-on = 'http://localhost:8000'
wait-on-timeout = '30'
record = true // highlight-line
После этого возьмите две вещи из вашей учетной записи Cypress: ID проекта и ключ записи, оба в разделе Настройки проекта.
Теперь обновите cypress.config.ts
:
import { defineConfig } from "cypress"
export default defineConfig({
projectId: "a7bq2k",
e2e: {
baseUrl: "http://localhost:8000",
},
})
И объявите переменную окружения сборки CYPRESS_RECORD_KEY в Netlify. Если вы не знаете, как это сделать, прочитайте о том, как это сделать здесь. Установите CYPRESS_RECORD_KEY на ключ записи, который вы получили из учетной записи Cypress.
🔌 Мы подключены! Теперь мы записываем наши тесты из процесса сборки Netlify и можем просматривать эти видео и скриншоты, когда нам это необходимо.
Интеграция с GitHub
🍒 И в качестве вишенки на вершине мы можем интегрироваться с GitHub. Посмотрите на приложение Cypress GitHub App.
Я рекомендую вам сделать это и настроить ваш репозиторий таким образом, чтобы процесс Netlify стал зеленым, прежде чем вы сможете объединиться. Это обеспечит еще один уровень защиты.
Подведение итогов
Вот так я настраивал Cypress в своих последних проектах. Я постоянно учусь, развиваюсь и открыт для других способов работы, поэтому я буду рад, если вы поделитесь со мной информацией о том, как можно сделать по-другому.
Я надеюсь, что это может помочь некоторым людям. Если есть вопросы, дайте мне знать, и я постараюсь ответить.
Комментируйте ниже или пишите мне в Твиттере.