在反应中测试嵌套组件回调

Posted

技术标签:

【中文标题】在反应中测试嵌套组件回调【英文标题】:testing nested component callback in react 【发布时间】:2019-11-27 18:16:25 【问题描述】:

给定一个呈现Baz 嵌套组件的Foo(根)组件,其中Baz 有一个名为onOperationDone 的属性,它接受回调。

class Foo extends React.Component 
  constructor(props) 
    super(props);

    this.onOperationDone = () => 
      console.log("do something");
    
  

  render() 
    return(
      <div>
        <Baz onOperationDone=this.onOperationDone />
      </div>
    )
  

需要执行哪些步骤才能使Baz 执行回调以确保回调被调用(使用enzyme 或test-utils)?

你能帮我理解应该怎么做吗?

【问题讨论】:

【参考方案1】:

在您的代码中,我觉得有些事情有点奇怪。我假设他们在创建问题时是拼写错误,但我仍然会对它们发表评论。如果我假设的内容恰好是错误的,请说出来,我会相应地编辑我的答案。

首先,您的渲染方法没有返回任何内容。通常,JSX 应该放在 return 语句中。

其次,onOperationDone 方法在类的构造函数中声明。这意味着每次创建类的新实例时,也会创建该方法(占用必要的内存量)。相反,我会在类的原型中定义方法,以便在所有实例之间共享。

考虑到这一点,您的类看起来像(请注意,我已删除构造函数,因为它只会调用 super 并且是自动完成的):

class Foo extends React.Component 
    onOperationDone() 
        console.log("do something");
    

    render() 
        return (
            <div>
                <Baz onOperationDone=this.onOperationDone />
            </div>
        );
    
 

现在,为了测试Baz 组件调用onOperationDone 属性时调用FooonOperationDone 方法,我将在Foo onOperationDone 方法上设置一个间谍来检查它叫做。然后,我会搜索Baz 元素并调用它的onOperationDone

有了酵素,你可以做到:

it('the child calls its parent callback', function() 
    jest.spyOn(Foo.prototype, 'onOperationDone');

    const wrapper = shallow(<Foo />);
    wrapper.find(Baz).prop('onOperationDone')();
    expect(Foo.prototype.onOperationDone).toHaveBeenCalledTimes(1);
);

类实例的窥探方法

如果您试图监视属于您的类实例的方法(无论是通过在构造函数中定义方法,如您的情况,还是通过使用 class fields),事情变得有点棘手.

假设您试图在初始代码中监视onOperationDone

export default class Foo extends React.Component 
    constructor(props) 
        super(props);

        this.onOperationDone = () => 
            console.log("do something");
        ;
    

    render() 
        return (
            <div>
                <Baz onOperationDone=this.onOperationDone />
            </div>
        );
    

如果您尝试使用相同的方法来监视prototype,但监视实例方法,则它将不起作用:

it('the child calls its parent callback', function() 
    const wrapper = shallow(<Foo />);
    const instance = wrapper.instance();
    jest.spyOn(instance, 'onOperationDone');

    wrapper.find(Baz).prop('onOperationDone')();
    expect(instance.onOperationDone).toHaveBeenCalledTimes(1);
);

它将失败,说明未调用 spied 方法(尽管您会看到日志“做某事”)。

这是因为当你对Foo组件进行浅渲染时,会创建一个新的onOperationDone方法并将其添加到实例中,然后调用render方法并将onOperationDone作为prop分配给Baz 组件。

接下来,您正在监视实例方法(使用jest.spyOn),但这会创建一个新方法来包装您的原始onOperationDone,以便跟踪它被调用的次数和其他统计信息.问题是,Baz 道具没有改变,它是对原始方法的引用,而不是包装的方法。所以包装的方法永远不会被调用。

为了克服这个问题,我们需要强制更新组件(以便包装的 onOperationDone 被分配为 Baz 组件的道具。为此,我们有酶的浅渲染器的 update 方法。不幸的是,好像更新方法不总是force a re-render。

所以一种解决方法是调用setProps 方法来强制更新。最终的测试代码应该是这样的:

it('the child calls its parent callback', function() 
    const wrapper = shallow(<ChildComponentCallbackInstance />);
    const instance = wrapper.instance();
    jest.spyOn(instance, 'onOperationDone');

    // wrapper.update(); would be the ideal method to call
    wrapper.setProps();

    wrapper.find(Baz).prop('onOperationDone')();
    expect(instance.onOperationDone).toHaveBeenCalledTimes(1);
);

【讨论】:

感谢您的详细解释! 我已更改我的 sn-p 以包含 return 语句(当我删除代码时它被遗漏了)。您能否详细说明当onOperationDone 在构造函数中定义时(如在我的sn-p 中),如何做同样的事情? onOperationDone 修改状态,当onOperationDone 传递给Baz 时,我想避免使用bind() @Mr.,我更新了答案以考虑您要测试的情况。希望对您有所帮助! 对不起,你能提供功能组件的例子吗?请

以上是关于在反应中测试嵌套组件回调的主要内容,如果未能解决你的问题,请参考以下文章

反应 setState 嵌套数组回调

使用酶在反应测试中查找渲染子组件的数量?

在嵌套组件中反应组件道具

在功能组件中使用回调对 setState 进行反应

角反应嵌套形式

AngularJS - 嵌套组件回调没有使其通过组件工厂