Пирамида тестирования:
- Unit/Модульные тесты — проверяют компонент, модуль или функцию изолированно. Их должно быть как можно больше
- Интеграционные тесты — проверяют взаимдействие функций/классов/компоненов
- E2E/Сквозные тесты — проверяют систему полностью
Зачем нужны тесты:
- Гарантии корректной работы системы
- Снижение рабочей нагрузки на разработчиков и тестеров
- Ускорение разработки
- Ускорение рефакторинга
- Работает внутри браузера (в отличие от Selenium, например), задействуя его движок
- Поддержка браузерных DevTools
- Интерактивный визуальный UI
- Простой синтаксис
- Не подходит для кроссбраузерного тестирования — поддерживает только движок Chromium
- Загрузка пакета
yarn add cypress -D
- Интеграция в проект
"scripts": {
"cy:open": "cypress open", // Запуск GUI для использования в разработке
"cy:run": "cypress run" // Запуск headless для CI/CD
}
- Написание тестов:
- Тесты создаются в виде отдельных файлов с расширением .spec.js (также поддерживается .test.js)
- Тесты размещаются в папке
/integration
- Можно создать подпапки по страницам или бизнес-фичам
- Для констант домена и других опций можно использовать файл /cypress.json
Cypress использует синтаксис Mocha, который называется BDD
describe('My first test', () => { // Название для группы тестов
before(() => {...}) // хук для действий до начала тестов
it('Visits the example', () => { // Название теста
cy.visit('/'); // Последовательность действий через API
cy.get.('[data-cy="section"]').contains('Home page')
})
after(() => {...}) // хук для действий после тестов
})
Шпаргалка по синтаксису: https://gist.github.com/samwize/8877226
it('Clicks the menu link', () => {
cy.visit('/')
cy.get('href="/contacts"').click()
cy.location('pathname').should('eq', '/contacts') // получает путь и смотрит соответствие
})
[!INFO] Как и человек, Cypress может кликнуть только на кнопку, находящуются в поле видимости (но это можно обойти с помощью параметра
force
)
it('Input data', () => {
cy.visit('/contacts')
cy.get('[data-cy="email"]').type('asd@qwe.rty')
cy.get('[data-cy="comment"]').type('Lorem Ipsum')
cy.get('[data-cy="submit-button"]').click()
})
[!INFO] Cypress вводит текст в поля приближенно к тому, как это делает человек — посимвольно и с меняющейся скоростью
describe('Login page', () => {
it('Visits contact page', () => {
cy.visit('/auth')
})
it('Input data', () => {
cy.get('data-cy="username"').type('j0hnd0e')
cy.get('data-cy="password"').type('qwerty123')
cy.get('data-cy="submit-button"').click
cy.location('/pathname').should('eq', '/')
cy.contains('Welcome back, John Doe!')
})
})
it('Intercept', () => {
cy.visit('/search')
cy.intercept('GET', '/api/pa/search?q=*').as('search') // сохраняем метод и regex запроса, который нужно отловить
cy.get('[data-cy="query"]').type('Alabama')
cy.get('[data-cy="submit"]').click()
cy.wait('@search')
cy.get('[data="paragraph"]').find('a').should('have.length', 2)
})
Например, для проверок страниц, закрытых авторизацией, необходимо создать универсальную команду "сходи авторизуйся"
Команды находятся в файле /support/commands.js
Cypress.Commands.add('login', () => {
cy.request({
url: `${URL_API}/api/auth/login`,
method: 'POST',
failOnStatusCode: false,
body: {
username: 'j0hnd0e',
password: 'qwerty123'
}
})
.then(({body}) => {
cy.setCookie('access_token_key', body.access_token)
})
})
// Чтобы тесты не падали при ошибке команд, надо чтобы внизу было:
Cypress.on'uncaught:exception', (err, runnable) => {
return false
})
Теперь команда доступна в виде метода, который можно использоваться в хуке:
describe('My test', () => {
beforeEach(() => {
cy.login()
cy.visit('/')
})
it('Greetings', () => {
cy.contains('Hello')
})
})
Фикстура позволяет имитировать получения объект от бэкенда, если тот не готов к использованию:
Например, /fixtures/current_user.json
:
{
"id": 999,
"username": "Foo",
"email": "b@a.r",
}
Использование в тесте:
describe('Fixture use', () => {
beforeEach(() => {
cy.visit('/')
cy.fixture('current.user') // название файла
.then((user) => {
cy.intecept('GET', '/api/user/current', user).as('current_user')
// подменяем user на current_user
})
})
it('Home and greetings', () => {
cy.contains('Привет, Foo!')
})
})
- Разные
.it()
должны быть независимы друг от друга (для проверки можно попробовать сбрасывать страницу в начальное состояние при помощи хукаbeforeEach()
) - Использовать только относительные пути, добавив домен в качестве значени baseUrl в
/cypress.json
- В методе
.get()
можно указывать любые CSS-селекторы, но лучше использовать data-атрибуты и вырезать их при сборке билда - В аргументы
.wait()
передавайте используйте алиасы запроса.intercept
. Числа (как миллисекунды) можно передавать только при работе с анимациями
Документация Cypress — https://docs.cypress.io/ Источник конспекта — https://youtu.be/rZ_DIDNnlfs Шпаргалка по синтаксису — https://gist.github.com/samwize/8877226