反应测试;如何模拟 componentDidMount 或覆盖它?

React test; how to mock componentDidMount or overwrite it?

我正在尝试测试一个 React 组件。

var Component = React.createClass({

  componentDidMount: function () {

    return this.setState({
      name: 'blabla'
    });
  },

  render: function () {

    return (
      <h1>{this.state.name}</h1>
    );
  }
});

有没有办法在测试期间模拟 componentDidMount returns 或做什么?那会让我自己测试它,只测试组件渲染行为。

谢谢!

我找到了两种解决方法(我相信还有更多)。

1) 我已经使用 sinon-chai and required in the base element class and then use rewireifycomponentWillMount 方法上设置了一个 set a spy。这有效,但不确定您使用的是什么测试套件。

2) 可能是更简单的方法。就是直接使用 TestUtils 获取组件的实例,然后手动 运行 componentWillMount 方法。

第二种方式可能类似于(请原谅伪代码):

it('should call state when it first mounts', function () {
  var Component = require('../my-component');
  var component = TestUtils.renderIntoDocument(<Component />);

  component.setState({name: null});
  component.componentWillMount();

  expect(component.state.name).to.equal('blabla');
});

这里的想法,如果我理解正确的话,是你试图在你的测试中渲染一个组件之前将一个函数存根。在您的情况下, componentWillMount 在组件的生命周期中仅在呈现组件之前立即调用一次。所以你不能只渲染组件然后然后 stub out 函数,它必须在渲染发生之前完成。

我们以这些组件为例:

parent.js

var Child = require('./child.js');
var Parent = React.createClass({
    render : function () {
        return (
            <div className="parent">
                <Child/>
            </div>
        );
    }
});
module.exports = Parent;

child.js

var Child = React.createClass({
    test : function () {
        return true;
    },
    render : function () {
        if (this.test) {
            throw('boom');
        }
        return (
            <div className="child">
                Child
            </div>
        );
    }
});
module.exports = Child;

在这里,我们希望在渲染我们的子组件之前将 test 函数存根,否则它会爆炸。

我已经能够使用 jasmine-react 做到这一点。这些辅助函数在 运行 测试时提供了一些有用的功能,几乎到了 TestUtils 可以完全放弃的地步。

jasmineReact.render(component, [container]) 会将 component 的实例渲染到 [container] 中指定的 DOM 节点中。这类似于 TestUtils.renderIntoDocument(),除了它将组件呈现为附加的 DOM 节点而不是分离的 DOM 节点。它还将在测试完成后执行必要的清洁操作。

jasmineReact.spyOnClass(componentClass, functionName) 将删除属于组件 class 的特定函数。此行为一直保持到测试结束,这意味着您可以在 呈现组件之前 调用此函数。如果我没理解错的话,这就是你要找的。

因此,使用这两个辅助函数,我可以为上面显示的代码编写如下所示的测试:

var React = require('react/addons'),
    Parent = require('./parent.js'),
    Child = require('./child.js'),
    jasmineReact = require('jasmine-react-helpers');

describe('Parent', function () {
    it('does not blow up when rendering', function () {
        jasmineReact.spyOnClass(Child, 'test').and.returnValue(false);
        var parentInstance = jasmineReact.render(<Parent/>, document.body); //does not blow up
        expect(parentInstance).toBeTruthy(); //passes
    });
});

如果您有任何问题,请告诉我。

我更喜欢下面的方法,但需要使用 ES6 类。

// component.jsx
class Component extends React.Component {
    componentDidMount() { return this.setState({name: 'blabla'}); }
    render() { return (<h1>{this.state.name}</h1>); }
}

//component-spec.jsx
describe('Component', () => {
    it('does stuff', () => {
        let ComponentTest = class extends Component {
            componentDidMount() { 
                // your override here
            }
        };
        let component = TestUtils.renderIntoDocument(<ComponentTest />);
        //expect(component...).toEqual(...)
    });
});

重点是创建一个继承OriginalClass的按需ChildClass, 做任何覆盖然后 TestUtils.renderIntoDocument(<ChildClass />)