Учебник по библиотеке тестирования React - с примерами!

Модульное тестирование является основой веб-приложения, и в этой статье я поделюсь некоторыми примерами написания модульных тестов с помощью react-testing-library и помогу другим разработчикам написать тестовые примеры для своих веб-приложение.

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

Настройка очень проста.

Конфигурация

Добавьте сценарий в свой package.json

Ваш jest.config.js будет выглядеть так

Зависимости:

npm install --save-dev @testing-library/react
npm install --save-dev @testing-library/jest-dom
npm run test

Для получения дополнительной информации вы можете проверить инструкции по установке здесь
Также мы будем использовать jest-dom для написания тестов, которые подтверждают различные сведения о состоянии модели DOM.

Мы покроем

1. A basic test case
2. Why do we need MockedProvider
3. Component using Context
4. Await from reacting testing library
5. How to mock window objects

Базовый тестовый пример

import React from 'react';
const MyComponent = (props) => (
  const { logOut, loading } = props;
  return (
    <div data-testid="btnHeaderCategory">
      {loading ? <div data-testid="loading" 
         styleName={{"color":"green"}} >Loading</div> : 
         <div data-testid="headerText" onClick={logOut} >
          Mega menu
         </div> 
       }
    </div>
   );
};
export default MyComponent;

Чтобы написать тестовый пример для вышеуказанного компонента, я буду использовать некоторые методы, доступные в @testing-library/react, например render и getByText.
Кроме того, мы импортируемtoBeInTheDocument из jest-dom, чтобы проверить, доступен ли HTML в документе, ниже приведен тестовый пример.

import React from 'react';
import {toBeInTheDocument,toHaveStyle} from 
  '@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
expect.extend({ toBeInTheDocument, toHaveStyle });
const propData = {
   loading: false,
   data:[]
};
test('Test for loading state', () => {
   const logOut = jest.fn() // function mock
   render(
     <MyComponent {...propData} logOut={logOut}/>
   );
   const loadingElem = getByText('Loading');
   expect(loadingElem).toBeInTheDocument();
   expect(loadingElem).toHaveStyle(`color:green`);
});

В тестовом файле мы рендерим theMyComponent и передаем компоненту фиктивные реквизиты.

Мы использовали такие методы, как:
getByText, который захватывает элемент,
toBeInTheDocument проверяет наличие элемента HTML в DOM и toHaveStyle проверяет стиль элемента.

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

Зачем нам MockedProvider?

Мы получим некоторые данные для компонента, такие как имя и ссылка на твиттер, мы будем использовать useQuery из apollo-react-hooks.

import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import ComponentQuery from './ComponentQuery.graphql';
const MyComponent = () => (
  const { data } = useQuery(ComponentQuery);
  const userData = data?.userData; 
return (
    <div>
        <span>{userData.name}</span>
        <span>{userData.twitterLink}</span>
    </div>
  );
};
export default MyComponent;

Тестовый пример для вышеуказанного сценария

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
import ComponentQuery from './ComponentQuery.graphql';
import { MockedProvider } from '@apollo/react-testing';
expect.extend({ toBeInTheDocument});
const mockData = {
   request:{
      query: ComponentQuery,
      variables:{}
   },
   result: {
     data: {
       userData:{
          name: "Aatif",
          twitterLink: "https://twitter.com/aatifbandey",
          __typename: 'DynamicUserData'
       }
     }
   }
};
test('Test to check user name with mock data', () => {
   
   const { container } = render(
     <MockedProvider mocks={[mockData]}> 
       <MyComponent />
     </MockedProvider>  
   );
   const name = getByText(container, 'Aatif')
   expect(name).toBeInTheDocument();
});

Чтобы проверить поведение, мы должны предоставить фиктивные данные в соответствии с ответом gql, поэтому мы будем использовать MockedProvider из библиотеки apollo / response-testing для передачи фиктивный ответ для нашего компонента GQL
Примечание: макет должен быть точно таким же, как ответ на запрос GQL.

Кроме того, MockedProvider принимает еще одно свойство с именем typeName, которое принимает логическое значение, если переданный истинный фиктивный ответ должен содержать имя типа.

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

<MockedProvider mocks={[mockData, mockDataNew]}> 
   <MyComponent />
</MockedProvider>

Компонент, использующий контекст

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

import React, {useContext} from 'react';
import { GlobalContext } from './context/global';
const MyComponent = () => ( 
  const [globalState] = useContext(GlobalContext);
  const { name } = globalState;
  return (
    <div>
        <span>{name}</span>
    </div>
  );
};
export default MyComponent;

Прецедент

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render, getByText } from '@testing-library/react';
import MyComponent from '../MyComponent';
import GlobalProvider from './context/global';
expect.extend({ toBeInTheDocument});
test('Test to check name from global context', () => {
   const globalState = {
      name: "Aatif"
   };
   const { container } = render(
     <GlobalProvider value={globalState} > 
       <MyComponent />
     </GlobalProvider>  
   );
   const name = getByText(container, 'Aatif')
   expect(name).toBeInTheDocument();
});

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

Ожидайте ответа от библиотеки тестирования

У нас есть компонент, в котором мы отложим загрузку некоторого компонента Cart на основе данных prop, мы используем react-loadable для загрузки нашего компонента.

import React from 'react';
import Loadable from 'react-loadable';
const Cart = Loadable(() => import(/* webpackChunkName: "lazy" */ './cart'));
const MyComponent = (props) => ( 
  const { showCart } = prop;
  return (
    <div>
        <span>Lazy Laod component</span>
        {showCart ? <Cart /> : ""}
    </div>
  );
};
export default MyComponent;

Прецедент

import React from 'react';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import { render, 
   waitForElement, 
   getByTestId 
} from '@testing-library/react';
import MyComponent from '../MyComponent';
expect.extend({ toBeInTheDocument});
test('Test to load a lazy cart component', () => {
   
   const { container } = render(
       <MyComponent showCart={true} />
   );
   const elem = await waitForElement(
        () => getByTestId(container, 'cartElem')
   );
   expect(elem).toBeInTheDocument();
});

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

Мокинг оконных объектов

Мы напишем компонент, который будет перенаправлять страницу при нажатии кнопки, для перенаправления мы используем метод window.location.assign, ниже находится компонент

import React from 'react';
const MyComponent = () => ( 
  const redirect = () => {
    window.location.assign("/checkout");
  }
  return (
    <div>
        <button onClick={redirect} data-testid={"checkOut"}>   
          Checkout 
        </button>
    </div>
  );
};
export default MyComponent;

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

import React from 'react';
import { toHaveBeenCalledWith } from '@testing-library/jest-dom';
import { render, 
   fireEvent, 
   getByTestId 
} from '@testing-library/react';
import MyComponent from '../MyComponent';
expect.extend({ toHaveBeenCalledWith });
test('Test to load a lazy cart component', () => {
   jest.spyOn(window.location, 'assign').mockImplementation(l => {
     expect(l).toEqual('/checkout');
   });
   const { container } = render(
       <MyComponent />
   );
  const elem = getByTestId('checkOut');     
  fireEvent.click(elem);
  expect(window.location.assign).toHaveBeenCalledWith('/checkout');
});

Мы знаем, что наш тестовый пример запускается не в браузерах, он выполняется в среде NodeJS, поэтому у нас нет окна, определенного в среде узла.

Мы будем имитировать объект окна в соответствии со спецификацией компонента, как вы можете видеть, мы используем метод jest.SpyOn для имитации,
Чтобы имитировать щелчок по кнопке, мы использовали метод fireEvent и для проверки тестового примера мы используем метод toHaveBeenCalledWith для проверки перенаправления.

Это были некоторые основные примеры тестовых случаев с использованием RTL и jest-dom. Надеюсь, вам понравилась статья, поделитесь ею со своими друзьями и подписывайтесь на меня в twitter.