You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Jest或Sinon测试React组件componentDidMount中调用的方法?

问题背景与需求

我近期需要测试React组件的componentDidMount方法中是否调用了自定义方法getDocuments。组件核心代码如下:

componentDidMount() { this.props.actions.getDocuments(); }

组件通过Redux的connect、bindActionCreators关联actions,完整实现代码:

export class ComponentList extends React.Component { 
  constructor(props) { super(props) } 
  componentDidMount() { this.props.actions.getDocuments(); } 
  render() { 
    return ( 
      <div className="allowScroll"> .... </div> 
    ) 
  } 
} 
ComponentList.propTypes = { 
  document: PropTypes.object, 
  actions: PropTypes.object.isRequired 
}; 
const mapStateToProps = (state) => { 
  return { document: state.document } 
}; 
const mapDispatchToProps = (dispatch) => { 
  return { actions: bindActionCreators(componentActions, dispatch) }; 
} 
export default connect(mapStateToProps, mapDispatchToProps)(ComponentList)

我用Jest作为测试框架,用jest.fn()创建mock函数编写测试代码:

function setup(data) { 
  const props = { 
    session: {}, 
    actions: { getDocuments: jest.fn() } 
  }; 
  const wrapper = mount(<ComponentList {...props} />, { 
    context: { muiTheme: getMuiTheme() }, 
    childContextTypes: { muiTheme: React.PropTypes.object.isRequired } 
  } ); 
  return { props, wrapper }; 
} 
describe('component:', () => { 
  let component; 
  describe('Given that the container is loaded', () => { 
    beforeAll(() => { 
      component = setup(); 
    }); 
    it('should call the getDocuments to get the data', () => { 
      expect(component.props.actions.getDocuments).toHaveBeenCalled(); 
    }); 
  }); 
});

运行测试时报错:

TypeError: received.getMockName is not a function 
at Object.<anonymous> (src/containers/ComponentList/ComponentList.spec.js:61:158) 
at new Promise (<anonymous>) 
at <anonymous> 
at process._tickCallback (internal/process/next_tick.js:188:7)

改用Sinon替换Jest的mock函数后,仍报错:

expect(jest.fn())[.not].toHaveBeenCalled() 
jest.fn() value must be a mock function or spy. Received: function: [Function proxy] 
at Object.<anonymous> (src/containers/ComponentList/ComponentList.spec.js:61:158) 
at new Promise (<anonymous>) 
at <anonymous> 
at process._tickCallback (internal/process/next_tick.js:188:7)

请问是否可以使用Jest或Sinon实现该测试需求?如果可以,具体应该如何操作?


解决方案:用Jest或Sinon实现测试的正确方式

当然可以用Jest或者Sinon来完成这个测试,你遇到的报错主要是因为测试时没有正确处理Redux connect包裹后的组件,或者断言方式不匹配工具的要求。下面分别给出两种工具的正确实现步骤:

方法一:使用Jest完成测试

核心问题是你直接测试了被connect包裹后的默认导出组件,而不是原始的ComponentList类组件。connect会创建一个高阶组件,直接挂载它会导致你的mock props无法正确传递到原始组件中。正确的做法是测试未被connect包裹的原始组件:

  1. 在测试文件中导入原始的ComponentList(注意用花括号,不是默认导出):
import { ComponentList } from './ComponentList';
  1. 修改setup函数,确保mock函数正确传递,且挂载原始组件:
import React from 'react';
import { mount } from 'enzyme';
import { ComponentList } from './ComponentList';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

function setup() {
  const mockGetDocuments = jest.fn();
  const props = {
    session: {},
    actions: {
      getDocuments: mockGetDocuments
    },
    document: {} // 满足propTypes的要求,避免警告
  };

  const wrapper = mount(<ComponentList {...props} />, {
    context: { muiTheme: getMuiTheme() },
    childContextTypes: { muiTheme: React.PropTypes.object.isRequired }
  });

  return {
    props,
    wrapper,
    mockGetDocuments
  };
}

describe('ComponentList', () => {
  describe('When the component mounts', () => {
    it('should call getDocuments', () => {
      const { mockGetDocuments } = setup();
      expect(mockGetDocuments).toHaveBeenCalled();
    });
  });
});

这样修改后,你直接测试的是原始组件,mock函数会被正确调用,断言也能正常工作。之前的报错正是因为测试高阶组件导致mock没有到达目标方法。

方法二:使用Sinon完成测试

如果你更习惯用Sinon,只需要把Jest的jest.fn()替换成Sinon的sinon.spy(),同时注意用Sinon对应的断言方法(或适配Jest断言):

  1. 导入Sinon:
import sinon from 'sinon';
  1. 修改setup函数和断言逻辑:
function setup() {
  const mockGetDocuments = sinon.spy();
  const props = {
    session: {},
    actions: {
      getDocuments: mockGetDocuments
    },
    document: {}
  };

  const wrapper = mount(<ComponentList {...props} />, {
    context: { muiTheme: getMuiTheme() },
    childContextTypes: { muiTheme: React.PropTypes.object.isRequired }
  });

  return {
    props,
    wrapper,
    mockGetDocuments
  };
}

describe('ComponentList', () => {
  describe('When the component mounts', () => {
    it('should call getDocuments', () => {
      const { mockGetDocuments } = setup();
      // 用Sinon自带的断言更稳妥
      sinon.assert.calledOnce(mockGetDocuments);
      // 或者用Jest断言:expect(mockGetDocuments.calledOnce).toBe(true);
    });
  });
});

这里用Sinon自带的断言方法可以避免Jest识别不了Sinon spy的问题,如果你坚持用Jest的toHaveBeenCalled(),需要确保测试环境兼容,但用Sinon原生断言是更稳妥的选择。

额外注意点

  • 单元测试只关注组件本身的逻辑,所以一定要测试未被Redux connect包裹的原始组件,高阶组件的测试应该放在集成测试中。
  • 确保传递给组件的props满足propTypes的要求,避免控制台出现不必要的警告,也能让测试更严谨。

内容的提问来源于stack exchange,提问作者Sinha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:03:56