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

testing nested component callback in react

给定一个呈现 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 or test-utils)?

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

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

首先,您的渲染方法没有 returning 任何东西。通常,JSX 应该放在 return 语句中。

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

考虑到这一点,您的 class 看起来像(请注意,我删除了构造函数,因为它只会调用 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 个实例的间谍方法

如果您试图监视属于您的 class 实例的方法(无论是通过在构造函数中定义方法,如您的情况,还是通过使用 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);
});

它将失败,说明未调用侦测方法(尽管您会看到日志 "do something")。

这是因为浅渲染Foo组件时,会创建一个新的onOperationDone方法并添加到实例中,然后调用render方法,onOperationDone 被指定为 Baz 组件的道具。

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

为了克服这个问题,我们需要强制更新组件(以便包装的 onOperationDone 被分配为 Baz 组件的 prop。为此我们有 update method of enzyme's shallow renderer. Unfortunately, it seems that the update method does not always 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);
});