使用Jest测试React Component函数 [英] Test a React Component function with Jest

查看:249
本文介绍了使用Jest测试React Component函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我关注 Flux 架构。

我有一个显示秒数的指标,例如:30秒。每一秒它显示1秒减少,所以29,28,27直到0.当到达0时,我清除间隔,使其停止重复。而且,我触发了一个动作。调度此操作后,我的商店会通知我。因此,当发生这种情况时,我将间隔重置为30秒,依此类推。组件如下所示:

I have an indicator that shows a number of seconds, ex: 30 seconds. Every one second it shows 1 second less, so 29, 28, 27 till 0. When arrives to 0, I clear the interval so it stops repeating. Moreover, I trigger an action. When this action gets dispatched, my store notifies me. So when this happens, I reset the interval to 30s and so on. Component looks like:

var Indicator = React.createClass({

  mixins: [SetIntervalMixin],

  getInitialState: function(){
    return{
      elapsed: this.props.rate
    };
  },

  getDefaultProps: function() {
    return {
      rate: 30
    };
  },

  propTypes: {
    rate: React.PropTypes.number.isRequired
  },

  componentDidMount: function() {
    MyStore.addChangeListener(this._onChange);
  },

  componentWillUnmount: function() {
    MyStore.removeChangeListener(this._onChange);
  },

  refresh: function(){
    this.setState({elapsed: this.state.elapsed-1})

    if(this.state.elapsed == 0){
      this.clearInterval();
      TriggerAnAction();
    }
  },

  render: function() {
    return (
      <p>{this.state.elapsed}s</p>
    );
  },

  /**
   * Event handler for 'change' events coming from MyStore
   */
  _onChange: function() {
    this.setState({elapsed: this.props.rate}
    this.setInterval(this.refresh, 1000);
  }

});

module.exports = Indicator;

组件按预期工作。现在,我想用Jest测试它。我知道我可以使用renderIntoDocument,然后我可以setTimeout为30s并检查我的component.state.elapsed是否等于0(例如)。

Component works as expected. Now, I want to test it with Jest. I know I can use renderIntoDocument, then I can setTimeout of 30s and check if my component.state.elapsed is equal to 0 (for example).

但是,我是什么想在这里测试是不同的事情。我想测试是否调用刷新函数。此外,我想测试当我的经过状态为0时,它会触发我的TriggerAnAction()。好的,我尝试做的第一件事:

But, what I want to test here are different things. I want to test if refresh function is called . Moreover, I'd like to test that when my elapsed state is 0, it triggers my TriggerAnAction(). Ok, for the first thing I tried to do:

jest.dontMock('../Indicator');

describe('Indicator', function() {
  it('waits 1 second foreach tick', function() {

    var React = require('react/addons');
    var Indicator = require('../Indicator.js');
    var TestUtils = React.addons.TestUtils;

    var Indicator = TestUtils.renderIntoDocument(
      <Indicator />
    );

    expect(Indicator.refresh).toBeCalled();

  });
});

但是在编写npm test时收到以下错误:

But I receive the following error when writing npm test:

Throws: Error: toBeCalled() should be used on a mock function

我在ReactTestUtils上看到了一个 mockComponent 功能但是给出了解释,我不确定它是否是我需要的。

I saw from ReactTestUtils a mockComponent function but given its explanation, I am not sure if it is what I need.

好的,在这一点上,我被卡住了。任何人都可以告诉我如何测试上面提到的两件事吗?

Ok, in this point, I am stuck. Can anybody give me some light on how to test that two things I mentioned above?

这是我试图运行的测试(参见某些内容中的评论):

That's the test I am trying to run (see comments in some lines):

jest.dontMock('../Indicator');

describe('Indicator', function() {
  it('waits 1 second foreach tick', function() {

    var React = require('react/addons');
    var Indicator = require('../Indicator.js');
    var TestUtils = React.addons.TestUtils;

    var refresh = jest.genMockFunction();
    Indicator.refresh = refresh;

    var onChange = jest.genMockFunction();
    Indicator._onChange = onChange;

    onChange(); //Is that the way to call it?

    expect(refresh).toBeCalled(); //Fails
    expect(setInterval.mock.calls.length).toBe(1); //Fails

    // I am trying to execute the 1 second timer till finishes (would be 60 seconds)
    jest.runAllTimers();

    expect(Indicator.state.elapsed).toBe(0); //Fails (I know is wrong but this is the idea)
    expect(clearInterval.mock.calls.length).toBe(1); //Fails (should call this function when time elapsed is 0)

  });
});

我仍然误解了一些事情......

I am still misunderstanding something...

推荐答案

看起来你走在正确的轨道上。为了确保每个人都在这个答案的同一页面上,让我们一些术语。

It looks like you're on the right track. Just to make sure everyone's on the same page for this answer, let's get some terminology out of the way.

模拟:一个有行为的函数由单元测试控制。您通常使用mock函数替换某些对象上的实函数,以确保正确调用mock函数。除非你在该模块的名称上调用 jest.dontMock ,否则Jest会自动为模块上的每个函数提供模拟。

Mock: A function with behavior controlled by the unit test. You usually swap out real functions on some object with a mock function to ensure that the mock function is correctly called. Jest provides mocks for every function on a module automatically unless you call jest.dontMock on that module's name.

组件类:这是 React.createClass 返回的内容。您可以使用它来创建组件实例(它比这更复杂,但这足以满足我们的目的)。

Component Class: This is the thing returned by React.createClass. You use it to create component instances (it's more complicated than that, but this suffices for our purposes).

组件实例:实际渲染组件类的实例。这是您在调用 TestUtils.renderIntoDocument 或许多其他 TestUtils 函数后获得的。

Component Instance: An actual rendered instance of a component class. This is what you'd get after calling TestUtils.renderIntoDocument or many of the other TestUtils functions.

在您的问题的更新示例中,您正在生成模拟并将它们附加到组件而不是组件的实例此外,您只想模拟要监视或以其他方式更改的功能;例如,你模拟 _onChange ,但你真的不想,因为你希望它能正常运行 - 它只是刷新你想要模拟。

In your updated example from your question, you're generating mocks and attaching them to the component class instead of an instance of the component. In addition, you only want to mock out functions that you want to monitor or otherwise change; for example, you mock _onChange, but you don't really want to, because you want it to behave normally—it's only refresh that you want to mock.

这是我为这个组件写的一组测试;评论是内联的,所以如果您有任何问题,请发表评论。此示例和测试套件的完整工作源位于 https: //github.com/BinaryMuse/so-jest-react-mock-example/tree/master ;你应该能够克隆它并运行它没有任何问题。请注意,我必须对组件进行一些小的猜测和更改,因为并非所有引用的模块都在您的原始问题中。

Here is a proposed set of tests I wrote for this component; comments are inline, so post a comment if you have any questions. The full, working source for this example and test suite is at https://github.com/BinaryMuse/so-jest-react-mock-example/tree/master; you should be able to clone it and run it with no problems. Note that I had to make some minor guesses and changes to the component as not all the referenced modules were in your original question.

/** @jsx React.DOM */

jest.dontMock('../indicator');
// any other modules `../indicator` uses that shouldn't
// be mocked should also be passed to `jest.dontMock`

var React, IndicatorComponent, Indicator, TestUtils;

describe('Indicator', function() {
  beforeEach(function() {
    React = require('react/addons');
    TestUtils = React.addons.TestUtils;
    // Notice this is the Indicator *class*...
    IndicatorComponent = require('../indicator.js');
    // ...and this is an Indicator *instance* (rendered into the DOM).
    Indicator = TestUtils.renderIntoDocument(<IndicatorComponent />);
    // Jest will mock the functions on this module automatically for us.
    TriggerAnAction = require('../action');
  });

  it('waits 1 second foreach tick', function() {
    // Replace the `refresh` method on our component instance
    // with a mock that we can use to make sure it was called.
    // The mock function will not actually do anything by default.
    Indicator.refresh = jest.genMockFunction();

    // Manually call the real `_onChange`, which is supposed to set some
    // state and start the interval for `refresh` on a 1000ms interval.
    Indicator._onChange();
    expect(Indicator.state.elapsed).toBe(30);
    expect(setInterval.mock.calls.length).toBe(1);
    expect(setInterval.mock.calls[0][1]).toBe(1000);

    // Now we make sure `refresh` hasn't been called yet.
    expect(Indicator.refresh).not.toBeCalled();
    // However, we do expect it to be called on the next interval tick.
    jest.runOnlyPendingTimers();
    expect(Indicator.refresh).toBeCalled();
  });

  it('decrements elapsed by one each time refresh is called', function() {
    // We've already determined that `refresh` gets called correctly; now
    // let's make sure it does the right thing.
    Indicator._onChange();
    expect(Indicator.state.elapsed).toBe(30);
    Indicator.refresh();
    expect(Indicator.state.elapsed).toBe(29);
    Indicator.refresh();
    expect(Indicator.state.elapsed).toBe(28);
  });

  it('calls TriggerAnAction when elapsed reaches zero', function() {
    Indicator.setState({elapsed: 1});
    Indicator.refresh();
    // We can use `toBeCalled` here because Jest automatically mocks any
    // modules you don't call `dontMock` on.
    expect(TriggerAnAction).toBeCalled();
  });
});

这篇关于使用Jest测试React Component函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆