Привет, Хабр! Сегодня рассмотрим как автоматизировать тестирование React-приложений с инструментом Cypress.
Для начала работы с Cypress React-проекте, первым делом нужно установить сам пакет. Это можно сделать с помощью npm или Yarn:
npm install cypress --save-dev
# или
yarn add cypress --dev
После установки, нужно будет проинициализировать Cypress, что создаст базовую структуру папок и файлов конфигураций:
npx cypress open
Команда создат все необходимые файлы и откроет пользовательский интерфейс Cypress, где можно увидеть все тесты и управлять ими.
Структура будет выглядеть следующим образом:
cypress/fixtures/: папка для хранения фикстур — файлов с данными, которые могут быть использованы в тестах для имитации ответов сервера, загрузки данных и т. п
cypress/integration/: здесь располагаются тесты. Cypress автоматически поиск тестов в этой директории.
cypress/plugins/: папка для настройки плагинов, которые могут изменять поведение Cypress на более низком уровне, о них поговорим позже.
cypress/support/: содержит файлы, которые выполняются перед загрузкой тестов. Это идеальное место для размещения повторно используемых функций или настроек, доступных глобально во всех тестах.
cypress.json: файл конфигурации Cypress, где можно задать различные настройки, такие как базовые URL-адреса, время ожидания для тестов, переменные окружения и др.
package.json: стандартный файл Node.js, содержащий информацию о проекте и его зависимостях. В этом файле также можно настроить скрипты для запуска тестов.
Пример базовой конфигурации cypress.json:
{
"baseUrl": "http://example.com",
"defaultCommandTimeout": 10000,
"pageLoadTimeout": 30000,
"viewportWidth": 1280,
"viewportHeight": 720,
"testFiles": "**/*.spec.js"
}
Здесь:
baseUrl
задает базовый URL, к которому будут добавляться относительные URL в ваших тестах.defaultCommandTimeout
задает таймаут по умолчанию для команд Cypress в миллисекундах (в данном случае 10 секунд).pageLoadTimeout
задает максимальное время ожидания загрузки страницы (30 секунд).viewportWidth
иviewportHeight
задают ширину и высоту окна браузера для выполнения тестов.estFiles
указывает шаблон для файлов с тестами (в данном случае, все файлы с расширением .spec.js в любых подпапках).
Основной синтаксис и структура тестов в Cypress
Cypress использует цепочку команд. Тесты обычно организованы с помощью функций describe
и it
, которые являются частью глобального API Mocha.
Пример базового теста:
describe('Login Test', function() {
it('Visits the login page', function() {
cy.visit('https://example.com/login') // переход на страницу логина
cy.get('input[name=username]').type('user1') // вводим имя пользователя
cy.get('input[name=password]').type('password123') // вводим пароль
cy.get('form').submit() // отправка формы
cy.url().should('include', '/dashboard') // проверка URL после логина
})
})
Поиск элементов осуществляется с помощью команды cy.get()
, которая принимает строку селектора. Cypress поддерживает большинство селекторов CSS.
Взаимодействие с элементами возможно через команды вроде .click()
, .type()
, .check()
для разных типов элементов.
Ассерты проверяют ожидаемое состояние или поведение элементов. Cypress интегрируется с Chai, предоставляя множество утверждений через .should()
или .expect()
.
Примеры:
cy.get('.list-item').should('have.length', 5) // проверка количества элементов
cy.get('.alert').should('not.exist') // элемент не должен существовать
Cypress автоматически управляет асинхронностью, поэтому очень редко придется явно использовать async/await
. Например, если вы делаете запрос к API, Cypress дождется его завершения, прежде чем двигаться дальше:
cy.request('POST', '/api/users', {name: 'Alex'}).then((response) => {
expect(response.body).to.have.property('name', 'Alex') // проверка ответа
})
Cypress позволяет перехватывать HTTP-запросы, что делает возможным тестирование без реальных серверных ответов, используя моки:
cy.intercept('GET', '/api/users', {fixture: 'users.json'}).as('getUsers')
cy.wait('@getUsers').its('response.statusCode').should('eq', 200)
Можно использовать псевдонимы для сохранения данных и их последующего использования в тестах:
cy.get('input[name=firstname]').type('Jane').as('firstname')
cy.get('@firstname').should('have.value', 'Jane')
Cypress может делать снимки состояния приложения:
cy.visit('/settings')
cy.get('.theme').click()
cy.screenshot() // снимок изменённой темы
Примеры использования
Тестирование счётчика
Для компонента счётчика, который увеличивает или уменьшает значение при нажатии кнопок, тесты в Cypress могут проверять начальное состояние и реакцию на пользовательские действия:
describe('Counter Component', () => {
beforeEach(() => {
cy.visit('/');
});
it('should render the counter with initial value', () => {
cy.get('h1').contains('Counter: 0');
});
it('should increment the counter when increment button is clicked', () => {
cy.get('button').contains('Increment').click();
cy.get('h1').contains('Counter: 1');
});
it('should decrement the counter when decrement button is clicked', () => {
cy.get('button').contains('Increment').click();
cy.get('button').contains('Decrement').click();
cy.get('h1').contains('Counter: 0');
});
});
Тест проверяет правильность начального отображения компонента и его изменение при взаимодействии пользователя с элементами управления.
Тестирование формы входа
Тестирование формы входа включает в себя проверку ввода данных пользователя и реакции формы на отправку:
describe('Login Form', () => {
beforeEach(() => {
cy.visit('/login');
});
it('should allow a user to type in the username and password', () => {
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('qwerty');
});
it('should show an error message for invalid credentials', () => {
cy.get('input[name="username"]').type('invaliduser');
cy.get('input[name="password"]').type('wrongpassword');
cy.get('button[type="submit"]').click();
cy.get('.error-message').should('be.visible').and('contain', 'Invalid credentials');
});
it('should redirect to dashboard on successful login', () => {
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('qwerty');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});
Набор тестов проверяет как негативные, так и позитивные сценарии входа в систему.
Тестирование списка задач
Проверим добавление, удаление и валидацию элементов списка:
describe('Todo List Functionality', () => {
beforeEach(() => {
cy.visit('/todos');
});
it('displays the list of todos correctly', () => {
cy.get('li').should('have.length', 2); // предположим, что уже есть два элемента в списке
});
it('adds a new todo', () => {
const newItem = 'Complete the test';
cy.get('.new-todo-input').type(`${newItem}{enter}`);
cy.get('li').should('have.length', 3);
});
it('deletes a todo', () => {
cy.get('li').first().find('.delete-button').click();
cy.get('li').should('have.length', 1);
});
});
Различные плагины
Cypress не только позволяет тестировать приложение, но и расширяет его возможности через плагины, рассмотрим мои любимые.
cypress-axe
Плагин cypress-axe
интегрирует инструменты доступности axe-core
с Cypress, позволяя тестировать веб-страницы на предмет соответствия стандартам доступности:
it('Tests accessibility on the page', () => {
cy.visit('/your-page');
cy.injectAxe();
cy.checkA11y();
});
cypress-plugin-tab
Плагин добавляет поддержку событий клавиатуры, к примеру нажатие на клавишу Tab, что не поддерживается в базовой версии Cypress:
it('should be able to navigate fields with tab', () => {
cy.get('#first-input').type('info').tab().type('more info');
});
cypress-graphql-mock
Плагин cypress-graphql-mock
позволяет мокать ответы GraphQL, что хорошо при разработке и тестировании клиентских приложений, которые используют GraphQL для взаимодействия с сервером:
it('mocks GraphQL data', () => {
cy.mockGraphQL({ Query: { user: () => ({ id: 1, name: 'Artem' }) } });
cy.visit('/user-profile');
cy.contains('Artem');
});
Тест проверяет, что пользовательский интерфейс корректно отображает данные пользователя, мокая GraphQL запросы.
cypress-downloadfile
Плагин cypress-downloadfile
добавляет возможность скачивания файлов во время выполнения тестов:
it('downloads a file', () => {
cy.downloadFile('http://example.com/file.pdf', 'mydownloads', 'example.pdf');
cy.readFile('mydownloads/example.pdf').should('exist');
});
Здесь скачивается файл PDF с предоставленного URL, после он сохраняется в директорию и далее проверяет наличие файла в системе.
cypress-localstorage-commands
Плагин cypress-localstorage-commands
добавляет команды для управления данными в localStorage во время тестирования:
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it('tests local storage', () => {
cy.setLocalStorage('testKey', 'testValue');
cy.getLocalStorage('testKey').should('eq', 'testValue');
});
Здесь перед каждым тестом восстанавливается состояние localStorage, а после теста — сохраняется.
Статья подготовлена в преддверии старта курса React.js Developer. В рамках запуска курса пройдут бесплатные вебинары, на которые могут зарегистрироваться все желающие:
Оптимизация работы компонентов в React.js. Зарегистрироваться
Эффективное управление состоянием в React.js: useState и useReducer. Зарегистрироваться