Руководство по предотвращению ложных срабатываний

При создании веб-приложения вам необходимо убедиться, что ваши компоненты хорошо протестированы. Один из способов протестировать ваше приложение — использовать библиотеку типа @testing-library. Эта библиотека предоставляет вам функции для визуализации ваших компонентов и взаимодействия с ними, что упрощает тестирование вашего приложения.

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

Вот пример жестко закодированного data-testid:

import React from 'react';

interface ButtonProps {
  label: string;
  onClick: () => void;
}

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
  return (
    <button data-testid="submit-button" onClick={onClick}>
      {label}
    </button>
  );
};

export default Button;

Давайте проверим, работает ли он так, как ожидалось:

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';

describe('Button component', () => {
  it('should call onClick function when button is clicked', () => {
    const mockOnClick = jest.fn();
    const { getByTestId } = render(
      <Button label="Submit" onClick={mockOnClick} />
    );
    const submitButton = getByTestId('submit-button');
    fireEvent.click(submitButton);
    expect(mockOnClick).toHaveBeenCalled();
  });
});

Как видите, тест пройден, но если у вас есть несколько экземпляров этого компонента на странице, жестко заданный data-testid приведет к неправильным результатам теста.

Жесткое кодирование атрибутов data-testid в повторно используемых компонентах может привести к ложным срабатываниям при тестировании вашего приложения. Взгляните на этот пример:

import React from 'react';

interface ButtonProps {
  label: string;
  onClick: () => void;
}

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
  return (
    <button data-testid="submit-button" onClick={onClick}>
      {label}
    </button>
  );
};

const App = () => {
  return (
    <div>
      <Button label="Submit 1" onClick={() => console.log('Button 1 clicked')} />
      <Button label="Submit 2" onClick={() => console.log('Button 2 clicked')} />
    </div>
  );
};

export default App;

Теперь давайте напишем тест для этого компонента App, где мы хотим проверить, работают ли обе кнопки должным образом:

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from './App';

describe('App component', () => {
  it('should call the onClick function of both buttons when they are clicked', () => {
    const mockOnClick1 = jest.fn();
    const mockOnClick2 = jest.fn();
    const { getByTestId } = render(
      <App />
    );
    const submitButton1 = getByTestId('submit-button');
    fireEvent.click(submitButton1);
    expect(mockOnClick1).toHaveBeenCalled();
    const submitButton2 = getByTestId('submit-button');
    fireEvent.click(submitButton2);
    expect(mockOnClick2).toHaveBeenCalled();
  });
});

В этом случае, несмотря на то, что мы используем разные функции onClick для обеих кнопок, тест все равно будет пройден, поскольку обе кнопки имеют одинаковые data-testid из submit-button. Это наглядный пример того, как жестко закодированные тестиды могут приводить к ложным срабатываниям при тестировании.

Чтобы избежать этой проблемы, вы должны передавать атрибуты data-testid в качестве свойств, чтобы каждый экземпляр компонента мог иметь уникальное значение. Это поможет убедиться, что ваши тесты точны и надежны. Вот пример:

import React from 'react';

interface ButtonProps {
  label: string;
  onClick: () => void;
  testId: string;
}

const Button: React.FC<ButtonProps> = ({ label, onClick, testId }) => {
  return (
    <button data-testid={testId} onClick={onClick}>
      {label}
    </button>
  );
};

export default Button;

А вот обновленный тест для этого компонента:

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';

describe('Button component', () => {
  it('should call onClick function when button is clicked', () => {
    const mockOnClick = jest.fn();
    const { getByTestId } = render(
      <Button label="Submit" onClick={mockOnClick} testId="submit-button-1" />
    );
    const submitButton = getByTestId('submit-button-1');
    fireEvent.click(submitButton);
    expect(mockOnClick).toHaveBeenCalled();
  });
});

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