Тестируйте свои React-компоненты с помощью Nightwatch и Testing Library

от автора

Взгляд на популярную Testing Library с Nightwatch — и многое другое

Nightwatch + Testing Library

Nightwatch + Testing Library

Мы создадим подробный пример проекта React с Vite, а затем воспользуемся Nightwatch и Testing Library для тестирования этих компонентов. Мы используем Complex example, доступный в документах React Testing Library, написанный с помощью Jest.

В этом уроке мы рассмотрим следующее:

  1. Установим новый проект React с помощью Vite, который также используется внутри Nightwatch для компонентного тестирования

  2. Установим и сконфигурируем Nightwatch и Testing Library

  3. Сымитируем API запросы с помощью плагина @nightwatch/api-testingН

  4. Напишем комплексный React-компонентный тест, используя Nightwatch и Testing Library.

Шаг 0. Создание нового проекта

Для начала мы создадим новый проект с Vite:

npm init vite@latest

Выберите React и JavaScript при появлении запроса. Это создаст новый проект с React и JavaScript.

Шаг 1. Установка Nightwatch и Testing Library

Testing Library для React может быть установлена с помощью пакета @testing-library/react:

npm i @testing-library/react --save-dev

Чтобы установить Nightwatch, выполните команду инициализации:

npm init nightwatch@latest

Выберите React и JavaScript при появлении запроса. С помощью этого будет установлен nightwatch и плагин @nightwatch/react. Выберите браузер для установки драйвера. В этом примере мы будем использовать Chrome.

1.1. Установка плагина @nightwatch/testing-library

Начиная с версии 2.6, Nightwatch предоставляет свой плагин для использования запросов Testing Library. Потом он понадобится нам для написания нашего теста, поэтому давайте сейчас его установим:

npm i @nightwatch/testing-library --save-dev

1.2. Установка плагина @nightwatch/apitesting

Этот пример содержит заглушку сервера, которая необходима для тестирования компонента. Мы будем использовать встроенную заглушку сервера, которая поставляется с плагином @nightwatch/apitesting. Установим его с помощью следующей команды:

npm i @nightwatch/apitesting --save-dev

Шаг 2. Создание компонента Login

Мы будем использовать тот же компонент, что и в React Testing Library docs. Создаем новый файл src/Login.jsx и добавляем следующий код:

// login.jsx import * as React from 'react'  function Login() {   const [state, setState] = React.useReducer((s, a) => ({...s, ...a}), {     resolved: false,     loading: false,     error: null,   })    function handleSubmit(event) {     event.preventDefault()     const {usernameInput, passwordInput} = event.target.elements      setState({loading: true, resolved: false, error: null})      window       .fetch('/api/login', {         method: 'POST',         headers: {'Content-Type': 'application/json'},         body: JSON.stringify({           username: usernameInput.value,           password: passwordInput.value,         }),       })       .then(r => r.json().then(data => (r.ok ? data : Promise.reject(data))))       .then(         user => {           setState({loading: false, resolved: true, error: null})           window.localStorage.setItem('token', user.token)         },         error => {           setState({loading: false, resolved: false, error: error.message})         },       )   }    return (     <div>       <form onSubmit={handleSubmit}>         <div>           <label htmlFor="usernameInput">Username</label>           <input id="usernameInput" />         </div>         <div>           <label htmlFor="passwordInput">Password</label>           <input id="passwordInput" type="password" />         </div>         <button type="submit">Submit{state.loading ? '...' : null}</button>       </form>       {state.error ? <div role="alert">{state.error}</div> : null}       {state.resolved ? (         <div role="alert">Congrats! You're signed in!</div>       ) : null}     </div>   ) }  export default Login

Шаг 3. Создание компонента Test

Один из основополагающих принципов Testing Library заключается в том, что тесты должны максимально походить на то, как пользователи взаимодействуют с приложением. При написании компонентных тестов в Nightwatch с использованием JSX нам нужно написать тест как компонентную историю, используя формат Component Story Format, декларативный формат, представленный Storybook.

Это позволяет нам писать тесты, сосредотачиваясь на том, как компонент используется, а не на том, как он реализован, что соответствует философии Testing Library. Подробнее об этом можно прочитать в документации Nightwatch docs.

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

3.1. Тест входа с валидными данными

Создаем новый файл src/Login.spec.jsx и добавляем следующий код, который делает то же самое, что и комплексный пример, написанный с помощью Jest:

Чтобы визуализировать компонент с помощью JSX в Nightwatch, мы создаем некий экспорт для визуализированного компонента, при необходимости с набором реквизитов. Функции play и test используются для взаимодействия с компонентом и для верификации результатов.

  • play используется для взаимодействия с компонентом. Он выполняется в контексте браузера, поэтому мы можем использовать объект screen из Testing Library для запроса DOM и запуска событий;

  • test используется для проверки результатов. Он выполняется в контексте Node.js, поэтому мы можем использовать объект Nightwatch browser для запроса DOM и проверки результатов.

// login.spec.jsx import {render, fireEvent, screen} from '@testing-library/react' import Login from '../src/login'  export default {   title: 'Login',   component: Login }  export const LoginWithValidCredentials = () => <Login />; LoginWithValidCredentials.play = async ({canvasElement}) => {   //fill out the form };  LoginWithValidCredentials.test = async (browser) => {   // verify the results };

Добавление заглушки сервера

В примере используется заглушка сервера для имитации запроса логина. Мы будем использовать встроенную заглушку сервера, который идет с плагином @nightwatch/apitesting.

Для этого мы будем использовать хуки setup и teardown, которые мы можем написать прямо в тестовом файле. Оба хука выполняются в контексте Node.js.

Нам также необходимо установить конечную точку входа в http://localhost:3000/api/login в компоненте Login, который является URL-адресом заглушки сервера.

Завершаем файл теста

Полный файл теста будет выглядеть так:

// login.spec.jsx import {render, fireEvent, screen} from '@testing-library/react' import Login from '../src/Login'  let server; const token = 'fake_user_token'; let serverResponse = {   status: 200,   body: {token} };  export default {   title: 'Login',   component: Login,   setup: async ({mockserver}) => {     server = await mockserver.create();     server.setup((app) => {       app.post('/api/login', function (req, res) {         res.status(serverResponse.status).json(serverResponse.body);       });     });      await server.start(mockServerPort);   },    teardown: async (browser) => {     await browser.execute(function() {       window.localStorage.removeItem('token')       });          await server.close();   } }  export const LoginWithValidCredentials = () => <Login />; LoginWithValidCredentials.play = async ({canvasElement}) => {   //fill out the form   fireEvent.change(screen.getByLabelText(/username/i), {     target: {value: 'chuck'},   });    fireEvent.change(screen.getByLabelText(/password/i), {     target: {value: 'norris'},   });    fireEvent.click(screen.getByText(/submit/i)) };  LoginWithValidCredentials.test = async (browser) => {   const alert = await browser.getByRole('alert')   await expect(alert).text.to.match(/congrats/i)    const localStorage = await browser.execute(function() {     return window.localStorage.getItem('token');   });    await expect(localStorage).to.equal(fakeUserResponse.token) };

Дебагинг

Одним из основных преимуществ использования Nightwatch для компонентного тестирования, помимо наличия того же API, доступного для end-to-end тестирования, является то, что мы можем запускать тесты в реальном браузере, а не в виртуальной DOM среде, такой как JSDOM.

Это позволяет нам использовать Chrome Dev Tools для отладки тестов.

Например, давайте пойдем дальше и добавим оператор debugger в функцию LoginWithValidCredentials.play:

LoginWithValidCredentials.play = async ({canvasElement}) => {   //fill out the form   fireEvent.change(screen.getByLabelText(/username/i), {     target: {value: 'chuck'},   });    fireEvent.change(screen.getByLabelText(/password/i), {     target: {value: 'norris'},   });      debugger;      fireEvent.click(screen.getByText(/submit/i)) };

Теперь запустим тест с флагами --debug и --devtools

npx nightwatch test/login.spec.jsx --debug --devtools

Откроется новое окно Chrome с открытыми инструментами разработчика Dev Tools. Теперь мы можем установить точку останова в Dev Tools и пройтись по коду.

Дебагинг с Chrome DevTools

Дебагинг с Chrome DevTools

3.2. Тест логина с сервер исключением

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

Попробуем написать то же самое в Nightwatch. На этот раз мы будем использовать только функцию test, так как мы также можем взаимодействовать с компонентом. Как мы упоминали ранее, функция test выполняется в контексте Node.js и получает в качестве аргумента объект Nightwatch browser.

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

export const LoginWithServerException = () => <Login />; LoginWithServerException.preRender = async (browser) => {   serverResponse = {     status: 500,     body: {message: 'Internal server error'}   }; };  LoginWithServerException.test = async (browser) => {   const username = await browser.getByLabelText(/username/i);   await username.sendKeys('chuck');    const password = await browser.getByLabelText(/password/i);   await password.sendKeys('norris');    const submit = await browser.getByText(/submit/i);   await submit.click();    const alert = await browser.getByRole('alert');   await expect(alert).text.to.match(/internal server error/i);    const localStorage = await browser.execute(function() {     return window.localStorage.getItem('token');   });    await expect(localStorage).to.equal(token) };

Шаг 4. Запуск теста

Наконец, давайте запустим наш тест. Будет запущен LoginWithValidCredentials и LoginWithServerException компонент историй в Chrome.

npx nightwatch test/login.spec.jsx

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

[Login] Test Suite ──────────────────────────────────── ℹ Connected to ChromeDriver on port 9515 (1134ms).   Using: chrome (108.0.5359.124) on MAC OS X.  Mock server listening on port 3000    Running <LoginWithValidCredentials> component: ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── [browser] [vite] connecting... [browser] [vite] connected.   ✔ Expected element <LoginWithValidCredentials> to be visible (15ms)   ✔ Expected element <DIV[id='app'] > DIV > DIV> text to match: "/congrats/i" (14ms)   ✔ Expected 'fake_user_token'  to equal('fake_user_token'):     ✨ PASSED. 3 assertions. (1.495s)    Running <LoginWithServerException> component: ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── [browser] [vite] connecting... [browser] [vite] connected.   ✔ Expected element <LoginWithServerException> to be visible (8ms)   ✔ Expected element <DIV[id='app'] > DIV > DIV> text to match: "/internal server error/i" (8ms)   ✔ Expected 'fake_user_token'  to equal('fake_user_token'):     ✨ PASSED. 3 assertions. (1.267s)    ✨ PASSED. 6 total assertions (4.673s)

Заключение

Вот и все! Вы можете найти полный код этого примера в репозитории GitHub.Не стесняйтесь заходить в Nightwatch Discord, если у вас есть какие-либо вопросы или отзывы.

Также хочу порекомендовать вам бесплатный урок от OTUS, в рамках которого мы узнаем что из себя представляет PlayWright. Почему автотестеры все чаще стали выбирать именно его? Ознакомимся с его документацией, подключим к тестовому проекту и попробуем его особенности на практике.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/719266/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *