Автоматические тесты важны для большинства приложений.

В этой статье мы рассмотрим, как писать тесты для компонентов React.

Модули Mocking

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

Например, если у нас есть следующие компоненты:

Map.js

import React from "react";
import { LoadScript, GoogleMap } from "react-google-maps";
export default function Map(props) {
  return (
    <LoadScript id="script-loader" googleMapsApiKey="YOUR_API_KEY">
      <GoogleMap id="example-map" center={props.center} />
    </LoadScript>
  );
}

Contact.js

import Map from "./map";
export default function Contact({ name, email }) {
  return (
    <div>
      <address>
        {name} {email}
      </address>
      <Map center={props.center} />
    </div>
  );
}

Затем мы можем протестировать Contact компонент с имитацией Map компонента, написав:

Contact.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Contact from "./contact";
jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});
let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});
afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});
it("should render contact information", () => {
  const center = { lat: 0, long: 0 };
  act(() => {
    render(
      <Contact
        name="james"
        email="[email protected]"
        center={center}
      />,
      container
    );
  });
  expect(
    container.querySelector("address").textContent
  )
    .toContain("james [email protected]");
  expect(container.querySelector('[data-testid="map"]').textContent).toEqual(
    "0:0"
  );
});

У нас есть:

jest.mock("./map", () => {
  return function DummyMap(props) {
    return (
      <div data-testid="map">
        {props.center.lat}:{props.center.long}
      </div>
    );
  };
});

издеваться над компонентом Map.

Затем, когда мы вызываем render, мы выполняем рендеринг с DummyMap вместо фактического Map компонента.

События

Чтобы проверить события, мы можем отправлять реальные события DOM на элементы DOM.

Например, если мы хотим протестировать компонент Toggle:

import React, { useState } from "react";
export default function Toggle(props) {
  const [state, setState] = useState(false);
  return (
    <button
      onClick={() => {
        setState(previousState => !previousState);
        props.onChange(!state);
      }}
      data-testid="toggle"
    >
      {state ? "off" : "on"}
    </button>
  );
}

Затем мы можем добавить для него тестовый файл, написав:

Toggle.test.js

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Toggle from "./toggle";
let container = null;
beforeEach(() => {
  container = document.createElement("div");
  document.body.appendChild(container);
});
afterEach(() => {
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});
it("changes value when clicked", () => {
  const onChange = jest.fn();
  act(() => {
    render(<Toggle onChange={onChange} />, container);
  });
  const button = document.querySelector("[data-testid=toggle]");
  expect(button.innerHTML).toBe("on");
  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });
  expect(onChange).toHaveBeenCalledTimes(1);
  expect(button.innerHTML).toBe("off");
  act(() => {
    button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
  });
  expect(onChange).toHaveBeenCalledTimes(2);
  expect(button.innerHTML).toBe("on");
});

Мы имитируем метод onChange, который мы передаем как значение свойства onChange.

Затем получаем кнопку с селектором[data-testid=toggle] из компонента Toggle.

Затем мы можем получить содержимое кнопки и сколько раз onChange было вызвано после того, как мы вызывали dispatchEvent для отправки щелчка MouseEvent.

Нам нужно передать { bubbles: true }, чтобы React делегировал событие документу.

Заключение

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

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