使用 Jest 测试 React 组件功能 [英] Test a React Component function with Jest

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

问题描述

首先,我遵循 Flux 架构.

First of all, I am following the Flux architecture.

我有一个显示秒数的指示器,例如: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,然后我可以设置30s的Timeout并检查我的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)

  });
});

我还是有些误解...

推荐答案

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

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:行为受单元测试控制的函数.您通常使用模拟函数替换某个对象上的真实函数,以确保正确调用模拟函数.Jest 会自动为模块上的每个函数提供模拟,除非您对该模块的名称调用 jest.dontMock.

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.

Component Class:这是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.

在您的问题的更新示例中,您正在生成模拟并将它们附加到组件 class 而不是组件的 实例.此外,您只想模拟要监视或以其他方式更改的函数;例如,您模拟 _onChange,但实际上并不想这样做,因为您希望它正常运行——您只想模拟 refresh.

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 组件功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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