Как проверить взаимодействие пользователя с помощью библиотеки тестирования React

Когда вы тестируете разные компоненты в React, вам нужно будет имитировать взаимодействие пользователя с разными частями каждого компонента.

В этом учебнике я собираюсь показать вам несколько методов имитации взаимодействия пользователя с разными интерактивными элементами.

Прежде чем начать, убедитесь, что вы знаете, как структурировать тест в React и как писать простые тесты. Вы можете освежить эти знания, прочитав мой предыдущий пост о том, как писать модульные тесты в React.

У меня также есть задача для вас, поэтому не забудьте прочесть статью полностью.

Как настроить проект

Создайте свое приложение React с помощью команды create-react-app testing-user-interactions. Далее установите библиотеку user-event.

npm i user-event

Я предполагаю, что вы знаете, как использовать эту библиотеку. Если нет, я объяснил, как это работает в своем предыдущем руководстве.

На самом деле, эта библиотека имеет несколько функций, которые можно использовать для имитации пользователя. Мы рассмотрим все эти функции на примерах.

Прежде чем писать свои тесты, вам нужно будет выполнить следующие импорты в начале набора тестов (файла, в котором вы пишете все свои тесты).

import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

Вам также нужно будет импортировать компонент, который вы будете тестировать.

Для каждого теста мы будем симулировать взаимодействие пользователя и проверять, как ведет себя наш компонент.

Как тестировать select Взаимодействие элементов

В вашей заявке вы можете иметь select элементы, позволяющие пользователю выбрать что-либо из списка опций. Возьмем пример:

<select id='selectCity'>    
    <option> Mumbai</option>    
    <option> Bangalore</option>    
    <option> Chennai</option>
</select>

Обычно когда пользователь видит a select элемент, они щелкают его и выбирают подходящий вариант. Мы собираемся смоделировать такое же поведение и проверить, реагирует ли наш компонент соответственно. Например, если пользователь выбирает Chennaiпоказанное значение должно быть одинаковым.

Сначала давайте посмотрим, как запрашивать элемент. Давайте использовать getByRole чтобы запросить элемент. Вы можете использовать роль combobox для вышеперечисленного элемента.

screen.getByRole('combobox')

Что такое роль? Как узнать, какую роль можно использовать для элемента? В React для целей тестирования существует атрибут под названием role определен для каждого элемента HTML. Это облегчает запрос на элемент во время тестирования.

Теперь, чтобы узнать значение атрибута role для каждого элемента, можно обратиться к списку ролей здесь.

Скриншот-2023-02-18-12.37.44-PM
Снимок списка ролей

Если вы хотите спросить элемент привязки, содержащий href атрибут, вы можете использовать роль link. То же касается других элементов в списке.

Однако не определяйте явный атрибут роли своего элемента HTML, поскольку он уже определен для вас.

Давайте рассмотрим другой способ запроса элемента. Если их больше одного select элемент, который можно использовать getByLabelText(). Для этого добавьте метку, <label htmlFor=”selectCity”>Select City</label> для элемента и делать screen.getByLabelText(/select city/i). Вы получите элемент.

Теперь используйте selectOptions() метод библиотеки для имитации выбора опции пользователя. Он принимает три аргумента: элемент, значение и любые параметры.

userEvent.selectOptions(    
    screen.getByRole('combobox'),    
    'Chennai'
)

Затем добавьте утверждение в конце теста, чтобы проверить, имеет ли элемент то же значение, которое выбрал пользователь.

expect(screen.getByRole('combobox')).toHaveValue('Chennai')

Избранный пользователь Chennai и наши select элемент имеет одинаковое значение, поэтому тест пройден.

Как проверить изменение состояния

Теперь я обновлю состояние по изменению select значение элемента Во-первых, я вызову состояние cityName для отображения выбранной опции.

const [cityName, setCityName] = useState('Mumbai');
<h2> City Name:  {cityName} </h2>

Тогда я добавлю onChange атрибут с методом, обновляющим состояние.

onChange={(e) => { setCityName(e.target.value)}}

Чтобы написать тест для этого, выполните тот же процесс:

test("City Name header rendering", () => {    
    render(<SelectElement/>)    
    userEvent.selectOptions(        
        screen.getByRole('combobox'),        
        'Bangalore'    )    
    expect(screen.getByRole('heading',               
                     { name: /bangalore/i})).toBeInTheDocument();
})

Чтобы спросить h2 элемент, использование getByRole() с name вариант. The name опция добавляет еще один фильтр к запросу – если их несколько h2 элементов.

Как проверить событие наведения

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

<div className="tooltip">    
    <p> Hover over me</p>    
    <span className="tooltiptext">Tooltip text</span>
</div>

Существует также несколько стилей CSS для классов, определенных выше, но я не буду показывать их здесь. В основном, когда пользователь приводит курсор на текст Наводи на меня, текст подсказки виден.

Чтобы имитировать пользователя, который наводит курсор на текстовый элемент, используйте hover() метод. Добавьте утверждение в конце, чтобы проверить, видима ли подсказка.

test("Tooltip visible", () => {    
    render(<HoverEvent/>)
    userEvent.hover(screen.getByText(/hover over me/i))          
    expect(screen.getByText(/tooltip text/i)).toBeInTheDocument()
})

Тест пройдет, если всплывающая подсказка видна при наведении курсора. Если этого не происходит, вам нужно будет изменить свой код.

Как проверить событие загрузки файла

Еще одно важное событие – пользователь загружает файлы на ваш сайт. Это довольно распространенная функция в любой программе пользовательского интерфейса.

Давайте иметь input типа file.

<label htmlFor="singleFile">Upload File</label>
<input type="file" id='singleFile' />

В тесте вам нужно будет имитировать, как пользователь загружает файл. Использовать upload() способ сделать это:

test("Test single file upload", () => {    
    render(<FileUpload/>)    
    const file = new File(['hello'], 'hello.png', {type: 'image/png'})   
    userEvent.upload(screen.getByLabelText(/upload file/i), file)  
    expect(screen.getByLabelText(/upload file/i).files[0]).toEqual(file)
})

Сначала создайте макет файла и загрузите его. Затем сделайте утверждение, что файл, присутствующий во входных данных, соответствует нашему загруженному файлу. Это да, поэтому тест пройден.

Теперь давайте рассмотрим пример загрузки нескольких файлов.

<label htmlFor="multipleFiles">Upload multiple files</label>
<input type="file" id='multipleFiles' multiple />

В нашем тесте загрузите несколько файлов.

test("Test multiple file uploads", () => {    
    render(<FileUpload/>)    
    const files = [        
           new File(['file1'], 'file1.png', {type: 'image/png'}),       
           new File(['file2'], 'file2.png', {type: 'application/pdf'})   
    ]    
    userEvent.upload(screen.getByLabelText(/upload multiple files/i), 
                        files)    
    const fileInput = screen.getByLabelText(/upload multiple files/i)  
    
    expect(fileInput.files.length).toBe(2);   
    expect(fileInput.files[0]).toEqual(files[0]);   
    expect(fileInput.files[1]).toEqual(files[1]);
})

В утверждениях проверьте количество файлов и соответствуют ли оба файла загруженным. Таким образом все наши тесты проходят со 100% покрытием.

Теперь пора для вызова. Просмотрите мои публикации о загрузке нескольких файлов и различные примеры useState хука и напишите модульные тесты для всех функций. Загрузите код на GitHub и поделитесь им со мной.

Как проверить поведение формы

Важной особенностью большинства веб-приложений является обработка форм. При написании тестов для приложения React очень вероятно, что вам понадобится проверить поведение вашего компонента с помощью форм.

В нашем примере давайте иметь форму с двумя input поля, один select поле и кнопку «Отправить».

Во-первых, давайте определим состояния для соответствующих полей и дополнительное состояние для указания, отправляется ли форма.

const [name, setName] = useState('')
const [password, setPassword] = useState('')
const [country, setCountry] = useState('')
const [formSubmitted, setFormSubmitted] = useState(false)
<form>    
    <input onChange={(e) => {setName(e.target.value)}}             
           placeholder="Enter name"/>    
    <input onChange={(e) => {setPassword(e.target.value)}}                          placeholder="Enter password"/>    
    <label htmlFor="selectCountry"> Select Country </label>    
    <select id='selectCountry'             
            onChange={(e) => {setCountry(e.target.value)}}>       
        <option>India</option>        <option>Japan</option>       
        <option>China</option>        <option>USA</option>       
        <option>England</option>    
    </select>    
    <button onClick={handleSubmit}> Submit </button>
</form>

В onChange атрибут, сделать обновление состояния. В конце выведите текст об успешной отправке формы.

{formSubmitted && 'Form Submitted'}

Кроме того, давайте добавим проверку в handleSubmit метод. Если проверка не удается, отобразится список ошибок. Создайте другое состояние для хранения ошибок.

const [errors, setErrors] = useState([])

Теперь введите handleSubmit метод.

const handleSubmit = (event) => {    
    event.preventDefault();    
    const errorList=[];    
    if(name == '' || password == '' || country == '') {        
        errorList.push('Please fill all the details')    
    }        
    const regex = /^[a-z]*$/i;    
    if(!name.match(regex)) {        
        errorList.push('Please enter a valid name')    
    }    
    if(errorList.length != 0) {        
        setErrors(errorList);        
        setFormSubmitted(false);        
        return;      
    }     
    setErrors([]);    
    setFormSubmitted(true)
}

Во-первых, мы сделаем event.preventDefault() который отменяет какие-либо события, препятствующие отправке формы. Затем мы добавим несколько проверок ошибок и добавим все ошибки в состояние errors. В конце концов, если нет ошибок, то установите errors в пустой массив и установите formSubmitted как true.

Мы также отобразим список всех ошибок, которые будут видны по нажатию кнопки «Отправить».

errors.map(err => (    
    <p>        
    	{err}   
    </p>
))

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

test("Form getting submitted with correct input values", () => {
    render(<FormBehaviour/>)

    userEvent.type(screen.getByPlaceholderText(/enter name/i), 'kunal')
    userEvent.type(screen.getByPlaceholderText(/enter password/i),                                   'pass')
    userEvent.selectOptions(screen.getByLabelText(/select country/i),                               'India')
    userEvent.click(screen.getByText(/submit/i))

    expect(screen.getByText(/form submitted/i)).toBeInTheDocument();
})                                                              

Использовать type и click методы в userEvent библиотека для имитации ответных действий. В конце добавьте утверждение, чтобы проверить, соответствует ли текст регулярному выражению /form submitted/i видно в документе, поскольку наша логика отображает текст, как только посылается форма.

1*Tb0jZPtQRAJwlrlWpEhUiw
Тестовое покрытие для компонента поведения формы

Наш тест прошел, но все еще есть некоторые незакрытые строчки. Мы проверили поведение обычной формы. Однако у нас также есть механизмы обработки ошибок. Итак, вам также нужно написать тесты, охватывающие различные сценарии ошибок.

Во-первых, сымитируйте, как пользователь нажимает кнопку отправки, ничего не вводя.

test("Empty fields error shown", () => {    
    render(<FormBehaviour/>)    
    userEvent.click(screen.getByText(/submit/i))    
    expect(screen.getByText(/please fill all the details/i))                         .toBeInTheDocument()
})

После нажатия кнопки «Отправить» должно появиться сообщение об ошибке с просьбой заполнить все данные. Сформулируйте то же утверждение.

Пока не все линии охвачены. У нас еще один сценарий ошибки. Что делать, если пользователь вводит неверное имя?

test("Invalid name error shown", () => {    
    render(<FormBehaviour/>)    
    userEvent.type(screen.getByPlaceholderText(/enter name/i), '@###')       userEvent.type(screen.getByPlaceholderText(/enter password/i),                                    'pass')    
    userEvent.selectOptions(screen.getByLabelText(/select country/i),                                  'India')    
    userEvent.click(screen.getByText(/submit/i))    
    expect(screen.getByText(/Please enter a valid name/i))
          .toBeInTheDocument();
})

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

1*QEBA48iEtB15EmuQrs-DTQ
100% покрытие для компонента поведения формы

Теперь все тесты прошли со 100% покрытием.

Вы можете найти полный код GitHub.

Вывод

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

Я также продемонстрировал поведение тестовой формы, охватывающей многие сценарии. Надеюсь, теперь вы знаете, как пользоваться userEvent библиотека для проверки поведения вашего компонента с пользовательскими событиями.

Если вы не можете понять смысл или считаете объяснение неудовлетворительным, свяжитесь со мной и дайте мне знать. Новые идеи всегда ценятся! Не стесняйтесь общаться со мной в Twitter. А пока, до свидания!

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

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