Jest 单测最佳实践:如何在 Presenter、Container、Component 层清晰划分职责?

Jest 单测最佳实践:如何在 Presenter、Container、Component 层清晰划分职责?

前言

前端工程师在开发过程中,测试是必不可少的。测试可以保证代码质量,避免在上线之后出现各种故障。在前端领域,Jest 单测是一种非常流行的测试方法。在本文中,我们将讨论 Jest 单测在 Presenter、Container、Component 层如何清晰划分职责。

Presenter、Container、Component 层

在 React 中,Presenter、Container、Component 层是一种常见的代码架构模式。这个模式可以让我们的代码更清晰,并使我们的组件更容易维护。

Presenter 层主要负责将从 Container 层传递过来的数据展示给用户,但不负责处理数据的逻辑。Container 层负责处理数据的逻辑,即获取数据并将其传递给 Presenter 层。Component 层主要负责处理用户交互,例如点击事件等。

在进行 Jest 单测时,需要针对这三个层进行测试。下面将详细介绍如何在 Jest 单测中清晰划分职责。

Presenter 层的单测

Presenter 层主要负责展示数据,因此 Presenter 层的单测主要测试以下几个方面:

  • 当传递正确的数据时,Presenter 视图是否正确地渲染。
  • 当数据为空、undefined 或者无效时,Presenter 视图是否应该显示一个空状态的提示。

下面是一个 Presenter 层的单测示例代码:

import React from 'react';
import { shallow } from 'enzyme';
import MyPresenter from './MyPresenter';

describe('MyPresenter Test', () => {
  it('renders properly', () => {
    const props = {
      name: 'test name',
      age: 20,
    };
    const wrapper = shallow(<MyPresenter {...props} />);
    expect(wrapper).toMatchSnapshot();
  });
  
  it('renders empty state', () => {
    const props = {};
    const wrapper = shallow(<MyPresenter {...props} />);
    expect(wrapper.find('.empty-state')).toHaveLength(1);
  });
});

在上面的代码中,第一个测试用例测试了 Presenter 视图正确地渲染。第二个测试用例测试了当数据为空时,Presenter 应该显示一个空状态的提示。

Container 层的单测

Container 层主要负责获取数据。因此 Container 层的单测主要测试以下几个方面:

  • 当组件挂载时,是否进行了正确的数据获取。
  • 当传递正确的数据时,Container 是否将数据正确地传递给 Presenter。
  • 当获取数据失败时,Container 是否正确地处理了异常并显示错误提示。

下面是一个 Container 层的单测示例代码:

import React from 'react';
import { shallow } from 'enzyme';
import fetch from 'isomorphic-fetch';
import MyContainer from './MyContainer';

jest.mock('isomorphic-fetch');

describe('MyContainer Test', () => {
  beforeEach(() => {
    fetch.mockClear();
  });
  
  it('fetches data on mount', async () => {
    const props = {
      match: {
        params: {
          id: 'test-id',
        },
      },
    };
    
    fetch.mockResolvedValue({
      json: () => ({
        data: {
          name: 'test name',
          age: 20,
        },
      }),
    });
    
    const wrapper = shallow(<MyContainer {...props} />);
    
    await wrapper.instance().componentDidMount();
    expect(fetch).toHaveBeenCalledTimes(1);
    expect(wrapper.state('loading')).toBe(false);
    expect(wrapper.state('error')).toBe(false);
    expect(wrapper.state('data')).toEqual({
      name: 'test name',
      age: 20,
    });
  });
  
  it('passes props to presenter', () => {
    const props = {
      match: {
        params: {
          id: 'test-id',
        },
      },
      presenter: 'test-presenter',
    };
    
    const wrapper = shallow(<MyContainer {...props} />);
    expect(wrapper.find('test-presenter').prop('name')).toBe(undefined);
    expect(wrapper.find('test-presenter').prop('age')).toBe(undefined);
    
    wrapper.setState({
      data: {
        name: 'test name',
        age: 20,
      },
    });
    
    expect(wrapper.find('test-presenter').prop('name')).toBe('test name');
    expect(wrapper.find('test-presenter').prop('age')).toBe(20);
  });
  
  it('renders error state', () => {
    const props = {
      match: {
        params: {
          id: 'test-id',
        },
      },
    };
    
    fetch.mockResolvedValue({
      ok: false,
      statusText: '404 Not Found',
    });
    
    const wrapper = shallow(<MyContainer {...props} />);
    
    expect(wrapper.find('.error-state')).toHaveLength(0);
    expect(wrapper.instance().getCurrentState()).toMatchObject({
      loading: false,
      error: true,
    });
    expect(wrapper.find('.error-text').text()).toBe('404 Not Found');
  });
});

在上面的代码中,第一个测试用例测试了组件在挂载时是否进行了正确的数据获取。第二个测试用例测试了 Container 是否将数据正确地传递给 Presenter。第三个测试用例测试了当获取数据失败时,Container 是否正确地处理了异常并显示错误提示。

Component 层的单测

Component 层主要负责用户交互。因此 Component 层的单测主要测试以下几个方面:

  • 当用户交互时,是否调用了正确的函数。
  • 当用户提交表单时,是否防止了表单的默认行为。
  • 当调用函数时,是否传递了正确的参数。

下面是一个 Component 层的单测示例代码:

import React from 'react';
import { shallow, mount } from 'enzyme';
import MyComponent from './MyComponent';

describe('MyContainer Test', () => {
  it('calls function on click', () => {
    const props = {
      onClick: jest.fn(),
    };
    
    const wrapper = shallow(<MyComponent {...props} />);
    wrapper.find('.my-button').simulate('click');
    expect(props.onClick).toHaveBeenCalledTimes(1);
  });
  
  it('prevents default on submit', () => {
    const props = {
      onSubmit: jest.fn(),
    };
    
    const wrapper = mount(<MyComponent {...props} />);
    const form = wrapper.find('.my-form');
    form.simulate('submit');
    expect(props.onSubmit).toHaveBeenCalledTimes(1);
    expect(form.instance().submit.called).toBe(false);
  });
  
  it('calls function with correct params', () => {
    const props = {
      onClick: jest.fn(),
      name: 'test name',
      age: 20,
    };
    
    const wrapper = shallow(<MyComponent {...props} />);
    wrapper.find('.my-other-button').simulate('click');
    expect(props.onClick).toHaveBeenCalledWith({
      name: 'test name',
      age: 20,
    });
  });
});

在上面的代码中,第一个测试用例测试了当用户点击按钮时是否调用了正确的函数。第二个测试用例测试了当用户提交表单时是否防止了表单的默认行为。第三个测试用例测试了当调用函数时是否传递了正确的参数。

总结

在 Jest 单测中,Presenter、Container、Component 层需要分别进行单独的测试。Presenter 层的单测主要测试展示数据时是否正确,Container 层的单测主要测试获取数据时是否正确,Component 层的单测主要测试用户交互时是否被正确地处理。合理划分职责并针对每一层进行正确的测试,可以使我们的代码更清晰,更易于维护和测试。

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


纠错反馈