Первая альфа-версия функции тестирования компонентов стала доступна в версии Cypress 4.5.0. Это стало событием для многих, но я тогда не обратил внимание на этот шум. Что вполне нормально, поскольку компонентное тестирование всегда было больше уделом разработчиков, чем тестировщиков. Но тестировщики тоже могут проявлять к нему интерес. Теперь, когда функции компонентного тестирования Cypress стали общедоступными (достигли статуса General Availability — GA) в версии 11, я решил погрузиться в эту тему и выяснить, почему компонентное тестирование важно. Чем больше я разбирался, тем больше понимал его значимость. В этой статье я хотел бы поделиться своей точкой зрения.
Что такое компонент?
Компоненты можно сравнить с деталями Lego. Точно так же, как из деталей Lego можно строить игрушечные замки, автомобили и другие творения, веб-приложения строятся из компонентов. Как и детали Lego, компоненты бывают разных форм и размеров и могут служить разным целям. Некоторые из них используются только один раз, а некоторые — постоянно.
Каждый компонент обладает определенными визуальными и функциональными свойствами. Как и деталь Lego, компонент может быть обычным, который используется постоянно и меняется лишь незначительно, или уникальным, выполняющим определенную функцию.
Хочу показать вам, как это работает на примере простого приложения Vue.js. Давайте рассмотрим простой компонент Vue, который может выглядеть примерно так:
<template>
<button class=".green-button">{{ buttonText }}</button>
</template>
<script setup lang="ts">
defineProps({
buttonText: {
type: String,
default: "Hello world"
}
});
</script>
Этот компонент состоит из двух частей. Одной из них является часть <template>
, которая содержит динамический HTML. Вторая — это часть <script>
, которая содержит функцию TypeScript, определяющую свойства, которые мы хотим отобразить в <template>
. Строковое свойство buttonText
будет отображаться внутри элемента <button>
.
Это пример повторно используемого компонента. Я могу использовать элемент кнопки во всем своем приложении, но мне может понадобиться отображать для кнопки разные тексты. Всякий раз, когда мне нужно использовать этот компонент, я вызываю его так:
<MyButton buttonText="Click me!" />
Кнопка будет отображаться по-разному в зависимости от того, какое свойство ей передано. Вот несколько примеров:
![](https://habrastorage.org/getpro/habr/upload_files/e03/c13/1b4/e03c131b4d172b253deaaab666b9d32c.png)
Зачем тестировать компонент?
Если вам нужно убедиться, что эта кнопка хорошо выглядит с эмодзи, специальным символом или обычным текстом, что вы делаете? Вы можете следовать сквозному подходу, при котором нужно будет прокликать все приложение целиком, чтобы найти и проверить все кнопки, но это будет не очень эффективно. Это будет медленно и громоздко.
На помощь приходит компонентное тестирование.
Вместо того, чтобы работать со всем приложением целиком, при компонентном тестировании вы можете просто подключить компонент в изолированно. Благодаря этому вы сэкономите время, загрузив только интересующие вас части, и проведете тестирование гораздо быстрее. Или можно протестировать различные свойства одного и того же компонента и посмотреть, как они отображаются. Это может быть очень полезно в ситуациях, когда небольшие изменения влияют на большую часть приложения.
Представьте, что вы разработчик, которому нужно отрефакторить компонент. В процессе работы вы можете получить немедленную обратную связь обо всех возможных поломках. Другими словами, легче просто отобразить компонент с эмодзи, чем рыться в приложении в его поисках. Вот почему тестирование компонентов набирает популярность и почему оно становится одной из основных тенденций в области фронтенд-тестирования. Мы уже можем видеть различные реализации компонентного тестирования в таких инструментах, как Jest, Storybook, WebdriverIO, Playwright и Cypress.
Кнопок, кажется, не так уж и много, хотя...
Вы правы. Этот пример слишком прост, чтобы показать ценность компонентного тестирования.
Вместо кнопки представьте форму входа в систему, которая валидирует множество различных вводимых данных. Как проверить, что форма выбрасывает правильные ошибки?
Можно написать сквозной тест, который будет загружать экран входа, вводить правильные (и неправильные) учетные данные, проверять сообщения об ошибках, входить в систему, выходить из нее и т.д.
Или, вместо этого, можно открыть только компонент формы входа и попробовать разные варианты ввода без необходимости входа в систему или загрузки остальной части страницы входа. Загрузка одного компонента произойдет намного быстрее, чем загрузка всего приложения. Тестирование компонента в изоляции может принести много пользы, особенно в случаях, подобно вышеописанному.
Что делает Cypress отличным выбором для компонентного тестирования
Основное отличие Cypress от других инструментов заключается в том, что он запускает компонентные тесты в реальном браузере. Кроме этого, в вашем распоряжении остается вся мощь Cypress API для компонентных тестов. Вы можете кликать, вводить, делать assert-ы и перехватывать сетевые запросы. Вы можете следить за функциями или делать их заглушки. Это позволит вам добраться до некоторых действительно труднодоступных мест в приложении и протестировать их. Можно имитировать данные или состояние компонента, избегая сложного управления данными или настройки приложения.
Настройка тестирования компонентов в Cypress
Начиная с Cypress v10, настройка тестирования компонентов очень проста. Как только вы откроете графический интерфейс Cypress с помощью команды npx cypress open, перед вами откроется следующее окно:
![](https://habrastorage.org/getpro/habr/upload_files/fe7/5eb/26c/fe75eb26c204029e70a985fa35f056bd.png)
Вам нужно будет пройти через простой установочный мастер, который поможет установить все в соответствии с потребностями приложения. В отличие от сквозного, компонентное тестирование является специфическим для каждого фреймворка. Хотя принципы практически одинаковы для всех фреймворков, загрузчики немного различаются — для каждого из них нужен отдельный набор инструментов. Установочный мастер проведет вас через все шаги по настройке.
Фреймворк и сборщик
На следующем этапе Cypress просит выбрать фреймворк и сборщик. Он даже попытается автоматически определить фреймворки, используемые в приложении. Если вы создаете веб-приложения, вы, вероятно, знакомы с различными вариантами.
![](https://habrastorage.org/getpro/habr/upload_files/4e3/638/149/4e36381490afe59bbdc1b4da24bff94d.png)
Как тестировщик, я должен был ознакомиться с этими терминами, особенно с термином "сборщик” (bundler). Сборщик играет важную роль при создании современного веб-приложения. Говоря простым языком, он преобразует написанный разработчиком код в то, что может прочитать браузер. Как упоминалось ранее, компоненты разбивают приложение на небольшие "детали Лего". Часто это файлы .vue, .jsx или что-то подобное. Но они не будут работать в браузерах сами по себе. Сборщик превращает их в набор файлов .html, .js и .css, которые могут читать браузеры.
При выполнении компонентного теста Cypress использует сборщик для преобразования файла компонента в то, что браузер может прочитать, используя тот же сборщик, что и приложение.
Проект тестирования компонентов в Cypress
На основе введенных данных установочный мастер настроит проект. Взглянув на файл cypress.config.ts, можно увидеть, что конфигурация на самом деле довольно лаконична:
export default defineConfig({
component: {
devServer: {
framework: "vue",
bundler: "vite",
},
},
});
По умолчанию Cypress будет использовать опции, установленные в файле vite.config.ts нашего проекта, поэтому все, что было настроено для приложения, сразу же станет доступным и для тестов Cypress.
cy.mount()
Помимо инициализации настроек, Cypress создаст несколько вспомогательных файлов, один из самых важных — component.ts, расположенный в папке cypress/support.
import { mount } from 'cypress/vue'
// Расширьте пространство имен Cypress, включив в него определения типов для
// вашей пользовательской команды.
// В качестве альтернативы, могут быть определены в cypress/support/component.d.ts.
// с <reference path="./component" /> в верхней части вашей спецификации.
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
Cypress.Commands.add('mount', mount)
// Пример использования:
// cy.mount(MyComponent)
Этот файл будет содержать функцию подключения компонента для используемого фреймворка. Cypress поддерживает React, Angular, Svelte, Vue и даже такие фреймворки, как Next.js и Nuxt. По состоянию на 11-ю версию Cypress большинство из них вышли из бета-версии.
Как построен Cypress
Архитектура Cypress уникальна по сравнению с другими инструментами тестирования. В Playwright или Selenium целью является автоматизация браузера для выполнения определенных действий. В Cypress вы, по сути, создаете приложение для тестирования приложения.
Когда вы разрабатываете приложение, файлы собираются в пакет и открываются в браузере. Представьте себе стандартный npm run dev mode
.
В Cypress применяется практически тот же принцип. Тестовые файлы собираются в пакет и открываются в браузере. При тестировании компонентов вместо того, чтобы открывать приложение для сквозного тестирования, вы подключаете свой компонент. Довольно круто, как по мне.
Создание первого компонентного теста
После настройки конфигурации компонентного теста и команды cy.mount()
можно приступить к тестированию. Давайте посмотрим, как это сделать.
При сквозном тестировании обычно есть специальная папка, в которой находятся все тесты. Но при компонентном тестировании лучше разместить тесты прямо рядом с компонентами.
![](https://habrastorage.org/getpro/habr/upload_files/91f/3b8/548/91f3b85484fc5fb565d4e323da85a5c0.png)
При создании первого теста, примите стандартное соглашение об именовании. На самом деле, Cypress будет поощрять вас к этому в своей конфигурации по умолчанию, поскольку будет искать любые файлы .cy.js или .cy.ts. В принципе, добавление .cy идентифицирует файл как тест Cypress.
Однако этом можно изменить на .spec.ts в конфигурационном файле:
component: {
devServer: {
framework: 'vue',
bundler: 'vite',
},
setupNodeEvents(on, config) { },
specPattern: 'src/**/*.spec.ts',
}
Подключение компонента
Давайте теперь попробуем протестировать наш компонент кнопки. Тестирование компонентов в Cypress очень похоже на сквозное тестирование. В нем используются те же блоки it()
и describe()
, что и в Mocha, но вместо вызова cy.visit()
вы будете использовать cy.mount()
для подключения компонента в браузер.
import SaveButton from './SaveButton.vue'
it('display save button', () => {
cy.mount(SaveButton)
})
Обратите внимание, что каждый раз, когда мы хотим подключить компонент, нам нужно импортировать его в тестовый файл. Когда мы запустим этот тест в Cypress, он успешно подключит компонент.
![](https://habrastorage.org/getpro/habr/upload_files/cc3/1d8/fe2/cc31d8fe2829a66364dd5c32d81897ff.png)
Вы не ошибетесь, если на этом месте с удивлением поднимете брови, поскольку это не совсем то, чего вы ожидаете. Нашему компоненту не хватает CSS-стилей! Во многих случаях компоненты полагаются на какой-то другой ресурс. Это может быть модуль, пакет, менеджер состояний или CSS, как в данном случае. Это означает, что для того, чтобы тест компонента работал, часто бывает необходимо импортировать кучу всего.
CSS, вероятно, понадобится всем вашим компонентам, поэтому вы, возможно, захотите импортировать его в файл конфигурации component.ts. Это место для установки глобальной конфигурации для подключения всех компонентов.
import { mount } from 'cypress/vue'
import '@/index.css';
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
Cypress.Commands.add('mount', mount)
Передача свойств
Давайте теперь попробуем сделать с компонентом кое-что еще. Возможно, вы помните, что мы определяли свойство buttonText
, которое затем выводило текст на кнопке. Можно передать собственные свойства кнопке прямо в тесте, передав функции cy.mount()
объект props
с заданными свойствами:
import SaveButton from './SaveButton.vue'
it('SaveButton component', () => {
cy.mount(SaveButton, {
props: {
buttonText: 'Hello world'
}
})
})
Легко, правда? Теперь мы готовы тестировать различные версии кнопки, которые могут появиться в приложении. И поскольку мы находимся в тесте Cypress, мы можем использовать и команды Cypress. Например, можно проверить, что свойство buttonText
действительно отображается в кнопке:
import SaveButton from './SaveButton.vue'
it('SaveButton component', () => {
const buttonText = 'Hello world'
cy.mount(SaveButton, {
props: {
buttonText
}
})
cy.get('button').should('have.text', buttonText)
})
Компонентное тестирование и его важность
Компонентное тестирование позволяет рассмотреть отдельные компоненты приложения и убедиться, что они работают так, как ожидается. Оно помогает тестировщикам и разработчикам выявить и устранить проблемы с отдельными компонентами до того, как они станут более серьезными проблемами, влияющими на работу всего приложения.
Хотя компонентное тестирование по-прежнему относится в основном к сфере компетенций разработчиков, понимание этого типа тестирования полезно и для тестировщиков. В частности, изучение того, как создаются веб-приложения, прокачивает интуицию для поиска серьезных багов.
Что такое Appium? Как написать и запускать тесты на мобильном устройстве? Эти вопросы рассмотрим на открытом уроке, который состоится завтра вечером. На нем поговорим о мобильном автоматизированном тестировании, о библиотеке Appium, о ее преимуществах и недостатках, а также напишем простой автотест на приложение. Записаться можно на странице курса "Java QA Engineer. Professional".