A-Level. React. Tests.

Tests

Теми

  1. Коли потріні тести
  2. Підходи до тестування
  3. Піраміда тестування
  4. Інструменти
  5. Приклади, багато прикладів ))

Тестування

Тестуванні ПЗ - Процес дослідження, випробування програмного продукту для перевірки відповідності між реальним поведінкою програми та її очікуваною поведінкою.

wikipedia.org

Автоматизоване тестування

Автоматизоване тестування ПЗ - частина процесу тестування на етапі контролю якості в процесі розробки програмного забезпечення. Воно використовує програмні засоби для виконання тестів і перевірки результатів виконання, що допомагає скоротити час тестування і спростити його процес.

wikipedia.org

Коли потрібно і не потрібно тестувати

TDD (test-driven development)

tdd cycle
В основі лежать етапи:
  1. Пишемо наш тест.
  2. Запускаємо тест, і звісно він падає, бо фіча ще не готова.
  3. еалізуємо функцію, яка задовольняє даний тест.
  4. Рефакторимо.

Піраміда тестування

Піраміда тестування

- використовується для розподулення тестів по рівням застосунку.

Піраміда тестування

Інструменти

Етапи будь якого тест-кейса

  1. Arrange (підготовка) - налаштування тестових данних.
  2. Act (виконання) - виклик тестового методу/ф-ції/тощо.
  3. Assert (перевірка) - перевірка результатів та порівняння із очікуванними.

Приклад

            import { render, screen } from '@testing-library/react';
            import App from './App';
             
            test('renders learn react link', () => {
              render(<App />);
              const linkElement = screen.getByText(/learn react/i);
              expect(linkElement).toBeInTheDocument();
            });
        

Приклад

Пошук елементів

...ByLabelText label or aria-label content <label for="element" />
...ByPlaceholderText input placeholder value <input placeholder="name" />
...ByText element text content <p>Lorem ipsum</p>
...ByDisplayValue Current value of input element
...ByAltText img alt attribute <img alt="movie poster" />
...ByTitle title attribute or svg title tag <span title="Add" /> or <title />
...ByRole ARIA role <div role="dialog" />
...ByTestId data-testid attribute <div data-testid="some-message" />

Варианты поиска

no match 1 match 1+ match Await?
getBy... throw return throw No
findBy... throw return throw Yes
queryBy... null return throw No
getAllBy... throw array array No
findAllBy... throw array array Yes
queryAllBy... [] array array No

Поиск элементов

            screen.getByText(/Search:/i)
            screen.getByRole('textbox')
            screen.getByLabelText(/Search:/i)
            screen.getByPlaceholderText('search text...')
            screen.getByAltText('search image')
            screen.getByDisplayValue('123')
        
* getBy... - потрібно знайти елемент
* queryBy... - потрібно показати, що елемента немає в DOM
* allBy... - якщо елементів декілька

Список стверджень

Примеры

            expect(screen.getByPlaceholderText('search text...')).toBeRequired();
            // якщо нам потрібно заперечення, ставимо .not
            expect(screen.getByPlaceholderText('search text...')).not.toBeRequired();
            expect(screen.getByPlaceholderText('search text...')).toBeEmptyDOMElement();
            expect(screen.getByPlaceholderText('search text...')).toHaveAttribute('id');
        

Події. fireEvent

fireEvent - імітація взаємодії користувача з якимись елементами.

            test('change search input', () => {
              render(<Search/>);
              const inputElement = screen.getByRole('textbox');
              fireEvent.change(inputElement, {
                target: { value: 'React' }
              })
              expect(screen.queryByText(/Searches for React/i)).toBeInTheDocument();
            });
        

Приклад. Чекбокс.

            test('checkbox click', () => {
              const handleChange = jest.fn();
              const { container: { firstChild: checkbox } } = render(
                <input type="checkbox" onChange={handleChange} />
              )
              expect(checkbox).not.toBeChecked();
              fireEvent.click(checkbox);
              expect(handleChange).toBeCalledTimes(1);
              expect(checkbox).toBeChecked();
            })
        

Приклад. Фокус на інпуті

            test('input focus', () => {
              const { getByTestId } = render(
                <input type="text" data-testid="my-input" />
              )
              const input = getByTestId('my-input');
              expect(input).not.toHaveFocus();
              input.focus();
              expect(input).toHaveFocus();
            })
        

Події. userEvent.

import userEvent from '@testing-library/user-event'

Приклад. Чекбокс.

            test('click on checkbox', () => {
              const { container } = render(<input type="checkbox" />)
              const checbox = container.firstChild;
              expect(checbox).not.toBeChecked();
              userEvent.click(checbox);
              expect(checbox).toBeChecked();
            })
        

Приклад. Інпут

            test('onChange input', () => {
              const { getByTestId } = render(<Search />);
              const input = getByTestId('input-data');
              expect(input.value).toBeFalsy();
              userEvent.type(input, 'I love tests')
              expect(input.value).toBe('I love tests');
            })
        

Приклад. Інпут через screen

            test('onChange input with screen', () => {
              render(<Search />);
              expect(screen.queryByDisplayValue('I love tests')).not.toBeInTheDocument();
              userEvent.type(screen.getByTestId('input-data'), 'I love tests')
              expect(screen.getByDisplayValue('I love tests')).toBeTruthy();
            })
        

Приклад. Чекбоксу з натиснутим shift/ctrl

            test('click on checkbox', () => {
              const { container } = render(<input type="checkbox" />);
              const checkbox = container.firstChild;
              expect(checkbox).not.toBeChecked();
              // у випадку необхідності потрібно додати ніліштування для івента
              // наприклад: ми натискаємо на чекбокс з затиснутим ctrl/shift
              userEvent.click(checkbox, { ctrlKey: true, shiftKey: true });
              expect(checkbox).toBeChecked();
            })
        

Приклад. Даблклік

            test('dblClick on checkbox', () => {
              const onChange = jest.fn();
              const { container } = render(<input type="checkbox" onChange={onChange} />)
              const checkbox = container.firstChild;
              expect(checkbox).not.toBeChecked();
              userEvent.dblClick(checkbox);
              expect(onChange).toHaveBeenCalledTimes(2);
            })
        

Приклад. Ркх по натисканю Tab

            test('tab focus', () => {
              const { getAllByTestId } = render(<>
                <input data-testid='element' type="checkbox" />
                <input data-testid='element' type="radio" />
                <input data-testid='element' type="number" />
              </>)
              const [checkbox, radio, number] = getAllByTestId('element');
              userEvent.tab();
              expect(checkbox).toHaveFocus();
              userEvent.tab();
              expect(radio).toHaveFocus();
              userEvent.tab();
              expect(number).toHaveFocus();
            })
        

Приклад. Select.

            test('select option', () => {
              const { getByRole, getByText } = render(
                <select>
                  <option value="1">A</option>
                  <option value="2">B</option>
                  <option value="3">C</option>
                </select>
              )
              userEvent.selectOptions(getByRole('combobox'), '1'); // значення, яке юзер обирає
              expect(getByText('A').selected).toBeTruthy();
              userEvent.selectOptions(getByRole('combobox'), '2');
              expect(getByText('A').selected).toBeFalsy();
              expect(getByText('B').selected).toBeTruthy();
            })
        

Async

Ідея у наступному:
  1. Спочатку ми мокаємо функцію, через яку виконуємо запит
  2. Потім самі конструюємо відповідь від сервера/сервісу
  3. І перевіряємо стан нашого компонента або результат рендерингу.

Async. Приклад.

            test('fetch', async () => {
              window.fetch = jest.fn();
              window.fetch.mockResolvedValueOnce({
                json: async () => ({ ...mockData... }),
              });
             
              const { findAllByRole } = render(<Async/>);
             
              const listItemElement = await findAllByRole('listitem');
              expect(listItemElement).not.toHaveLength(0);
            });
        

Links