Как использовать Enzyme для тестирования React компонентов?react-75

Enzyme предоставляет три основных метода рендеринга компонентов для тестирования, каждый из которых подходит для разных сценариев.

1. Настройка окружения

Прежде чем начать, установите необходимые пакеты:

npm install enzyme enzyme-adapter-react-16 react-test-renderer

Создайте файл конфигурации setupTests.js:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

2. Shallow Rendering

Используется для изолированного тестирования компонентов без рендеринга дочерних компонентов.

import { shallow } from 'enzyme';

const Button = ({ children, onClick }) => (
  <button onClick={onClick} className="btn">
    {children}
  </button>
);

describe('Button Component', () => {
  it('renders children correctly', () => {
    const wrapper = shallow(<Button>Click me</Button>);
    expect(wrapper.find('.btn').text()).toEqual('Click me');
  });

  it('calls onClick handler when clicked', () => {
    const mockClick = jest.fn();
    const wrapper = shallow(<Button onClick={mockClick}>Click</Button>);

    wrapper.find('.btn').simulate('click');
    expect(mockClick).toHaveBeenCalledTimes(1);
  });
});

3. Full DOM Rendering

Необходим для тестирования компонентов с жизненным циклом (lifecycle) или интеграции с DOM API.

import { mount } from 'enzyme';

class Counter extends React.Component {
  state = { count: 0 };

  increment = () => this.setState({ count: this.state.count + 1 });

  render() {
    return (
      <div>
        <span className="count-value">{this.state.count}</span>
        <button className="increment-btn" onClick={this.increment}>
          Increment
        </button>
      </div>
    );
  }
}

describe('Counter Component', () => {
  it('increments count when button is clicked', () => {
    const wrapper = mount(<Counter />);

    expect(wrapper.find('.count-value').text()).toBe('0');
    wrapper.find('.increment-btn').simulate('click');
    expect(wrapper.find('.count-value').text()).toBe('1');
  });

  it('calls componentDidMount', () => {
    jest.spyOn(Counter.prototype, 'componentDidMount');
    mount(<Counter />);
    expect(Counter.prototype.componentDidMount).toHaveBeenCalledTimes(1);
  });
});

4. Static Rendering

Используется для анализа итогового HTML-вывода.

import { render } from 'enzyme';

const Message = ({ text }) => <div className="message">{text}</div>;

describe('Message Component', () => {
  it('renders correct message', () => {
    const wrapper = render(<Message text="Hello World" />);
    expect(wrapper.find('.message').text()).toBe('Hello World');
  });
});

5. Работа с хуками

Пример тестирования компонента с хуками:

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

const Counter = () => {
  const { count, increment } = useCounter();
  return (
    <div>
      <span data-testid="count">{count}</span>
      <button onClick={increment}>+</button>
    </div>
  );
};

describe('Counter with Hooks', () => {
  it('should increment counter', () => {
    const wrapper = mount(<Counter />);
    expect(wrapper.find('[data-testid="count"]').text()).toBe('0');

    wrapper.find('button').simulate('click');
    expect(wrapper.find('[data-testid="count"]').text()).toBe('1');
  });
});

6. Тестирование контекста

const ThemeContext = React.createContext('light');

const ThemedButton = () => (
  <ThemeContext.Consumer>
    {theme => <button className={`btn-${theme}`}>Button</button>}
  </ThemeContext.Consumer>
);

describe('ThemedButton', () => {
  it('renders with light theme by default', () => {
    const wrapper = shallow(<ThemedButton />);
    expect(wrapper.find('.btn-light')).toHaveLength(1);
  });

  it('renders with dark theme when provided', () => {
    const wrapper = mount(
      <ThemeContext.Provider value="dark">
        <ThemedButton />
      </ThemeContext.Provider>
    );
    expect(wrapper.find('.btn-dark')).toHaveLength(1);
  });
});

7. Советы по эффективному тестированию

  1. Используйте правильный метод рендеринга:

    • shallow для изолированных юнит-тестов
    • mount для интеграционных тестов
    • render для проверки HTML-вывода
  2. Избегайте тестирования реализации:

    • Фокусируйтесь на поведении, а не на внутренней структуре
    • Не злоупотребляйте доступом к state и методам компонента
  3. Используйте матчеры Jest:

    expect(wrapper.find('.item')).toHaveLength(3);
    expect(wrapper.props().disabled).toBeTruthy();
    

Резюмируем

Enzyme предоставляет мощный инструментарий для тестирования React-компонентов на разных уровнях. Выбирая между shallow, mount и render, вы можете контролировать глубину тестирования. Хотя современные проекты чаще используют React Testing Library, Enzyme остается отличным выбором для комплексного тестирования, особенно в legacy-проектах.