Jest 测试 React 中的高阶组件及修饰器

在 React 应用中,高阶组件 (Higher Order Component, HOC) 和修饰器 (Decorator) 是非常实用的工具。例如,它们可以帮助我们复用组件逻辑、管理组件的状态、控制渲染等等。不过,在使用 HOC 和修饰器时,我们也需要考虑如何进行测试。本文将详细介绍在使用 Jest 进行测试时,如何测试 React 中的高阶组件和修饰器,并提供一些示例代码以供参考。

高阶组件测试

测试 HOC 的返回值

在测试 HOC 时,我们通常会先测试其返回值。例如,我们有一个叫做 withAuthorization 的 HOC,其作用是判断用户是否具有某个权限,并根据情况进行渲染。

import React from 'react';
import { Redirect } from 'react-router-dom';

export default function withAuthorization(Component) {
  return class WithAuthorization extends React.Component {
    render() {
      const { isLoggedIn, permission } = this.props;
      if (isLoggedIn && permission === 'admin') {
        return <Component {...this.props} />;
      } else {
        return <Redirect to="/" />;
      }
    }
  };
}

我们可以写一个简单的测试,来验证这个 HOC 是否能够正确地根据用户权限渲染组件或进行重定向:

import React from 'react';
import { render } from '@testing-library/react';
import withAuthorization from './withAuthorization';

describe('withAuthorization', () => {
  const MockComponent = () => <div />;
  const MockComponentWithAuth = withAuthorization(MockComponent);

  test('renders component if user is logged in and has admin permission', () => {
    const { container } = render(<MockComponentWithAuth isLoggedIn permission="admin" />);
    expect(container.firstChild).not.toBeNull();
  });

  test('redirects to home page if user is not logged in', () => {
    const { container } = render(<MockComponentWithAuth />);
    expect(container.querySelector('Redirect')).not.toBeNull();
  });

  test('redirects to home page if user does not have admin permission', () => {
    const { container } = render(<MockComponentWithAuth isLoggedIn permission="user" />);
    expect(container.querySelector('Redirect')).not.toBeNull();
  });
});

在这个测试中,我们先声明了一个 Mock 组件作为 HOC 的参数,然后通过 withAuthorization 生成一个带有权限控制功能的新组件。我们接着编写了三个测试用例,分别测试了不同情况下 HOC 的渲染结果。通过这些测试,我们就可以保障我们的 HOC 可以按照预期工作了。

测试 HOC 包裹的组件

除了测试 HOC 的返回值,我们也需要测试 HOC 所包裹的组件是否能够正确地获得 HOC 传递的属性。例如,我们有一个叫做 withUser 的 HOC,其作用是将用户信息传递给被包裹的组件:

import React from 'react';

export default function withUser(Component) {
  return class WithUser extends React.Component {
    state = { user: null };

    componentDidMount() {
      // 获取用户信息的逻辑
      fetch('/api/user').then((res) => res.json()).then((user) => this.setState({ user }));
    }

    render() {
      return <Component user={this.state.user} {...this.props} />;
    }
  };
}

我们可以写一个简单的测试,来验证被包裹的组件能否正确地获取到 HOC 传递的参数:

import React from 'react';
import { render } from '@testing-library/react';
import withUser from './withUser';

describe('withUser', () => {
  const MockComponent = ({ user }) => <p>{user ? `Hello, ${user.name}!` : 'Please log in.'}</p>;
  const MockComponentWithUser = withUser(MockComponent);

  test('passes user info to wrapped component', () => {
    const user = { name: 'John' };
    const { container } = render(<MockComponentWithUser />);
    expect(container.firstChild.textContent).toBe('Please log in.');

    // 在组件挂载后,模拟获取用户数据并更新组件
    jest.spyOn(window, 'fetch').mockImplementation(() =>
      Promise.resolve({ json: () => Promise.resolve(user) })
    );
    await new Promise((resolve) => setTimeout(resolve, 1)); // 等待异步操作完成
    expect(container.firstChild.textContent).toBe('Hello, John!');

    window.fetch.mockRestore(); // 恢复 mock 以保障其他测试的正常运行
  });
});

在这个测试中,我们同样先声明了一个 Mock 组件作为 HOC 的参数,并通过 withUser 生成一个新的带有用户信息的组件。接着,我们编写了一个测试用例,验证 Mock 组件能否正确地获取到 HOC 传递的 user 属性。为了模拟异步操作,我们使用了 Jest 提供的 timer API 来暂停测试的执行,等到异步操作完成后再进行断言。通过这个测试,我们就可以保障被包裹的组件能够正确地获取到 HOC 传递的属性了。

修饰器测试

测试修饰器包裹的组件

在使用修饰器时,我们需要考虑如何测试被修饰的组件是否能够按照预期工作。例如,我们有一个叫做 withLoading 的修饰器,其作用是在组件加载时显示一个加载动画。

import React from 'react';

export default function withLoading(Component) {
  return class WithLoading extends React.Component {
    state = { isLoading: true };

    componentDidMount() {
      setTimeout(() => {
        this.setState({ isLoading: false });
      }, 1000);
    }

    render() {
      return this.state.isLoading ? <div>Loading...</div> : <Component {...this.props} />;
    }
  };
}

我们可以编写一个测试用例,验证被修饰的组件是否能够正确地显示加载动画并在加载完成后正常渲染。

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

describe('withLoading', () => {
  const MockComponent = () => <div data-testid="content">Content</div>;
  const MockComponentWithLoading = withLoading(MockComponent);

  test('displays a loading indicator when component is not loaded', () => {
    const { container } = render(<MockComponentWithLoading />);
    expect(container.textContent).toBe('Loading...');
  });

  test('shows the content when component is loaded', async () => {
    const { getByTestId, container } = render(<MockComponentWithLoading />);
    await act(() => new Promise((resolve) => setTimeout(resolve, 1500))); // 等待加载完成

    const content = getByTestId('content');
    expect(container.contains(content)).toBe(true);
  });
});

在这个测试中,我们同样声明了一个 Mock 组件,然后通过 withLoading 修饰它。我们编写了两个测试用例,分别测试组件在加载前和加载后的状态。对于加载前的状态,我们只需要验证组件是否正确地显示了加载动画;对于加载后的状态,我们需要等待异步操作完成后,验证组件是否能够正常渲染。通过这个测试,我们就可以保障修饰器可以正常地工作了。

总结

在 React 应用中,高阶组件和修饰器是非常实用的工具。在使用 Jest 进行测试时,我们需要考虑如何测试 HOC 和修饰器所包裹的组件及其返回值。对于 HOC,我们需要测试它的权限控制或参数传递等逻辑;对于修饰器,我们需要测试它是否能够正确地渲染组件或显示加载动画等。

在测试过程中,我们可以使用 Jest 提供的测试工具,如 renderact 等,来进行断言和模拟异步操作等操作。通过不断的测试及优化,我们可以确保我们的 HOC 和修饰器组件能够按照预期工作,并提高我们应用的可靠性和可维护性。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/659f69aeadd4f0e0ff80b944


纠错反馈