【问题标题】:Enzyme/Mocha: How to test a react component function by firing an onChange event from a child componentEnzyme/Mocha:如何通过从子组件触发 onChange 事件来测试反应组件功能
【发布时间】:2016-11-14 05:39:42
【问题描述】:

我正在使用酶/摩卡来测试我的反应成分。

我有一个正在测试的父组件。

let wrapper = mount(<Parent />);

这个父组件在它的渲染函数中有一个子组件

render: function() {
    <Child onChange={this.foo} />
},
foo: function() {
    console.log("I was called");
}

我希望孩子的 onChange 函数触发,以便我可以测试我父母的 foo 函数。

到目前为止,我还没有找到任何方法来做到这一点 - 我已经阅读了关于 sinon 和 stubing 的内容,但这主要是关于拦截函数而不是触发它们。

下面的测试

shallow(<Parent />).instance().foo();

是一个弱测试,因为它不测试连接我的孩子和父母的代码行,并且如果我没有为我的孩子编写单元测试,它也不会测试孩子的 onChange 功能。恕我直言 - 如果将我的组件分解给父母/孩子意味着更少的可测试性 - 那么这个框架有问题

任何帮助将不胜感激,谢谢

【问题讨论】:

  • 如果你在父测试中测试foo()方法,并且在子测试中孩子触发props.onChange()(使用sinon.spy())的能力,那么IMO你不需要测试鉴于孩子调用props.onChange,React 可以调用this.foo。就像您不需要测试 React 在状态更改时调用 render() 或在 props 更改时调用 componentWillReceiveProps() 一样。

标签: reactjs mocha.js enzyme


【解决方案1】:

这是我在很多测试中都会做的事情。我发现最适合我的方法是手动调用子组件的 onChange 处理程序,并根据您期望结果发生的行为做出断言。

假设您有一个如下所示的父组件:

import React from 'react';
import Child from './child';

export default class extends React.Component {
    render() {
        return (
            <div>
                <Child onChange={this.foo} />
            </div>
        );
    }

    foo() {
        console.log('bar');
    }
}

传递给 child 的 onChange 属性将在调用时记录字符串“bar”。这是我们要测试的行为。为此,我们需要采取以下步骤:

  1. Stub console.log 使用您选择的模拟库(我将在此示例中使用 Sinon)

  2. 创建 Parent 组件的浅实例,并获取对其 Child 的引用。

  3. 手动调用 Child 的 onChange 属性。

  4. 断言console.log 被调用过一次,并且只有一个参数:'bar'

我会这样做(使用 mocha 和 chai):

import Foo from './foo';

import React from 'react';
import {shallow} from 'enzyme';

import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chai, {expect} from 'chai';

describe('Foo', () => {
    let renderedElement;

    function renderComponent() {
        const componentElement = React.createElement(Foo);

        renderedElement = shallow(componentElement);
    }

    before(() => {
        chai.use(sinonChai);
    });

    it('should log the string "bar" when the child component is changed', () => {
        //step 1
        sinon.stub(console, 'log');

        //step 2
        renderComponent();
        const childComponent = renderedElement.props().children;

        //step 3
        childComponent.props.onChange();

        //step 4
        expect(console.log).to.have.callCount(1);
        expect(console.log).to.be.calledWith('bar');

        //clean up
        console.log.restore();
    });
});

我喜欢这种方法的原因是因为它正在测试组件的行为,而不是简单地测试它是作为一个恰好等于另一个函数的 prop 传递的函数。

【讨论】:

    【解决方案2】:

    尝试将您的测试分成几部分。比如……

    首先,测试预期的功能是否已传递给您的子组件:

    import { shallow } from 'enzyme';
    
    const actual = shallow(<Parent />);
    const expected = <Child onChange={actual.instance().foo} />
    
    expect(actual.matchesElement(expected)).true;
    

    当我正在测试的组件中呈现的内容很少时,我喜欢使用上面简单的matchesElement 方法,但您也可以使用find 选择器来查找Child 实例,然后对其进行测试。

    然后单独测试你的 foo 函数:

    import { shallow } from 'enzyme';
    
    const actual = shallow(<Parent />).instance().foo();
    const expected = 'expected return of foo'
    
    expect(actual).equals(expected);
    

    您可以单独测试您的 Child 组件以及它如何处理它的 onChange 属性。

    这里使用了一些酶 API:

    另请看:

    【讨论】:

    • 这不是一个坏主意,但总的来说它不是测试功能,而是比较字符串。匹配和简单检查每次提交都没有更改渲染函数之间有什么区别?此外,Parent 包含大约 10 个组件,我正在简化问题。
    • 查看我在示例下方的评论:对于任何复杂的渲染,最好使用find api 来定位子节点并断言它已按预期进行装饰。
    猜你喜欢
    • 2020-11-24
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 2019-04-12
    • 1970-01-01
    • 2016-12-04
    相关资源
    最近更新 更多