【问题标题】:React test; how to mock componentDidMount or overwrite it?反应测试;如何模拟 componentDidMount 或覆盖它?
【发布时间】:2015-11-02 15:24:08
【问题描述】:

我正在尝试测试一个反应组件。

var Component = React.createClass({

  componentDidMount: function () {

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

  render: function () {

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

有没有办法在测试期间模拟 componentDidMount 返回或执行的操作?这样我就可以自己测试它,只测试组件的渲染行为。

谢谢!

【问题讨论】:

  • 你不应该嘲笑它。如果你想确保你的组件正常工作,请模拟你作为道具传递给它的所有内容,模拟所有依赖项,但不要模拟组件本身。
  • 我不同意。如果 OP 试图编写单元测试来测试其组件内部的功能,那么他想要剔除可能影响这些测试的其他功能一点也不稀奇。 OP - 你使用什么测试框架?根据您的回答,我可能会为您提供解决方案。
  • 感谢您的反馈。 @MichaelParker 我正在使用业力和摩卡咖啡。
  • 不幸的是,我的解决方案仅适用于 Jasmine,因此如果您使用的是 Mocha,它不适用于您。如果你愿意,我无论如何都可以发布。
  • @MichaelParker 我不介意改用 jasmine,如果您能发布您的代码,我将不胜感激:)

标签: unit-testing testing mocking reactjs


【解决方案1】:

我更喜欢以下方法,但需要使用 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(&lt;ChildClass /&gt;)

【讨论】:

    【解决方案2】:

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

    我们以这些组件为例:

    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;
    

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

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

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

    jasmineReact.spyOnClass(componentClass, functionName) 将存根属于组件类的特定函数。这种行为一直保持到测试结束,这意味着您可以在渲染组件之前调用此函数。如果我理解正确,这就是你要找的东西。

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

    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
        });
    });
    

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

    【讨论】:

    • 谢谢! spyOnClass().and.returnValue() 看起来很棒而且非常强大!
    【解决方案3】:

    我找到了两种方法来解决这个问题(我相信还有更多)。

    1) 我在基本元素类中使用了sinon-chai 和required,然后使用rewireifycomponentWillMount 方法上设置了一个间谍。这可行,但不确定您使用的是什么测试套件。

    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');
    });
    

    【讨论】:

    • 谢谢@badAdviceGuy 我会试一试,让你知道
    • 使用TestUtils.renderIntoDocument会自动触发componentWillMount函数。不过,第一个选项看起来很有希望。
    • @MichaelParker,是的,您是对的,componentWillMount 将自动运行,但是在测试中您重置状态并再次手动调用该方法,然后再次测试状态component.componentWillMount() 等。跨度>
    • 是的,但是如果他想把它存根,让它什么都不做,你的第二个选择根本行不通。这就是我想要达到的目的。
    猜你喜欢
    • 1970-01-01
    • 2018-11-15
    • 2022-01-27
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-13
    相关资源
    最近更新 更多