【问题标题】:Is it possible to stub out or spy on the setState method in React when doing unit tests?在进行单元测试时,是否可以在 React 中存根或监视 setState 方法?
【发布时间】:2017-09-27 19:47:55
【问题描述】:

我的组件中有以下功能:

method(args) {
 fetch(args)
  .then((response) => {
    this.setState({
      element: response
      error: false
    });
  })
  .catch((error) => {
    this.setState({
      error: true
    });
  });
}

我正在尝试为它编写一个单元测试,因此我使用带有自定义响应的 fetch-mock 模拟了 fetch 调用。我想检查调用此方法时状态是否已更新并且它似乎不起作用。我正在使用酶、期望和 sinon,并且在设置 component.setState = sinon.spy 或将其存根时未能成功调用间谍。如何检查是否使用某些元素调用了 setState,或者我的单元测试方法是否错误?

【问题讨论】:

  • 如果您使用酶,您可以只获取组件的状态,为什么需要存根 setState 函数?为什么检查状态还不够?话虽如此,你可以嘲笑它,@ 987654322@。假设component = (shallow||render)(<Component>) 应该这样做
  • 状态是异步更新的,所以当我检查它时,它还不等于正确的值。我尝试了以下方法:` const setStateSpy = sinon.spy();` const component = shallow(<Main {...props} />); main.instance().setState = setStateSpy; expect(setStateSpy.called).toBe(true); 但这返回错误。
  • 您刚刚告诉我状态是异步更新的,如果您无法检查状态的值,您将无法检查是否调用了 setState。同样的原因也适用。如果是异步操作,您需要模拟异步操作或根据异步操作本身的性质设置超时。
  • 我确实模拟了异步操作,但 setState 方法也是每个反应的异步操作。我确实可以通过超时解决它,但这似乎是一个 hacky 解决方案,这就是我想监视 setState 方法的原因。

标签: unit-testing reactjs sinon enzyme


【解决方案1】:

在实例化组件之前,您似乎需要在原型中窥探/存根setState。我遇到了类似的问题,简单地监视实例的 setState 方法不起作用。基于https://medium.com/@tjhubert/react-js-testing-setstates-callback-907df1fe720d,以下是sinon的一种做法:

Component.js

...
method {
  this.setState({property:'value'})
}
...

Component.test.js

...
const setState = sinon.stub(Component.prototype, ‘setState’);
const component = shallow(<Component />);
component.instance().method();
expect(setState).to.be.calledOnce;
...

注意:我的用例是使用 jasmine,并避免渲染来测试行为,所以我知道的解决方案看起来像:

Component.prototype.setState = jasmine.createSpy();
const sut = new Component();
sut.method();
expect(sut.setState).toHaveBeenCalledWith({property:'value'});

【讨论】:

    【解决方案2】:

    假设在组件挂载时调用该方法并且该方法已被存根,试试这个:

    it('should update state with response', () => {
      const wrapper = mount(<Component />);
    
      return Promise.resolve().then(() => {
        expect(wrapper.state('error')).to.be.false;
      });
    });
    

    与在 setTimeout 中使用回调相比,返回一个 Promise 允许您以一种更简单的方式测试异步行为。

    我使用 sinon 作为存根,所以我在测试中会有这样的东西:

    sinon.stub(window, 'fetch').resolves(mockedResponse);
    

    【讨论】:

    • 感谢您的回答。我不知道您可以将 Promise 返回到单元测试,并且在该 Promise 解决后断言会起作用。让我的生活轻松多了。
    【解决方案3】:

    您可以暂时模拟/替换组件的setState() 方法并等待其下一次执行。这样的事情可能会奏效:

    function onNextSetState(component) {
      const instance = component.instance()
      const originalSetState = instance.setState.bind(instance)
      return new Promise(resolve => {
        instance.setState = (state, callback) => {
          originalSetState(state, (...args) => {
            instance.setState = originalSetState
            resolve()
            if (callback) {
              callback(...args)
            }
          }  
        }
      }
    }
    
    it('should update state with response', async () => {
      const component = mount(<Component />)
      component.instance().method()   // we assume this runs .setState inside()
      await onNextSetState(component) // we wait for the next re-render
    
      // we can be sure the state is what we want it to be:
      expect(component.state('error')).toBe(false)
    })
    

    请注意,如果您想多次等待,使用 Observable 代替 Promise 可能更容易/更好。

    【讨论】:

      【解决方案4】:

      我能够使用以下内容存根 setState:

      let setStateStub = sinon.stub(prototype, 'setState')
      

      完成存根后,您还需要 restore() 原始文件。 您可以通过在存根之后添加以下内容来做到这一点:

      setStateStub.restore()
      

      :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-16
        • 1970-01-01
        • 2011-11-15
        • 2011-11-16
        • 1970-01-01
        • 1970-01-01
        • 2019-12-13
        • 1970-01-01
        相关资源
        最近更新 更多