【问题标题】:Can't spy on an event handler even though I can spy on other methods即使我可以监视其他方法,也无法监视事件处理程序
【发布时间】:2017-06-20 08:41:13
【问题描述】:

我想使用 Jest/Jasmine/Enzyme 在 React 中测试一个事件处理程序。

MyComponent.js

import React from 'react';
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.clickHandler = this.clickHandler.bind(this);
        this.otherMethod  = this.otherMethod .bind(this);
    }

    clickHandler() { this.otherMethod(); }
    otherMethod() {}

    render() { return <div onClick={this.clickHandler}/>; }
}
export default MyComponent;

MyComponent.test.js

import React from 'react';
import {mount} from 'enzyme';
import MyComponent from './MyComponent';

it('should work', () => {
    const componentWrapper = mount(<MyComponent/>);
    const component = componentWrapper.get(0);

    spyOn(component, 'otherMethod' ).and.callThrough();
    spyOn(component, 'clickHandler').and.callThrough();

    componentWrapper.find('div').simulate('click');

    expect(component.otherMethod ).toHaveBeenCalled(); // works
    expect(component.clickHandler).toHaveBeenCalled(); // does not work
});

尽管我认为我对这两个组件方法的监视是相同的,但其中一个(对于otherMethod)有效,而另一个(对于clickHandler)无效。我显然 am 调用 clickHandlerotherMethod 如果我不是,则不会被调用,但 toHaveBeenCalled 不会以某种方式被 clickHandler 接听。为什么?

我知道我不必在otherMethod 上使用.bind(this).and.callThrough(),但我同时使用这两种方法只是为了同样对待这两种方法,并且在otherMethod 上使用它们实际上不应该产生任何效果区别。

This other SO answer 声明我必须在将函数附加为侦听器之前对其进行监视。如果这是我的问题,那么我不知道如何解决它:spyOn 语法需要该方法是其属性的对象(在这种情况下为component),但使用component 需要事先安装@987654337 @ 这迫使我首先附加监听器。

我的代码使用 React 可能是相关的(因此我将 reactjs 作为问题标签包含在内)但不知何故我对此表示怀疑。

【问题讨论】:

  • 主要问题是您试图窥探组件的内部。通常,您会向组件注入某种回调,或者作为 click 事件的结果进行异步调用。这些东西来自组件外部,很容易被监视(回调)或模拟(异步调用)。也许您的示例已简化,但您应该关注this.otherMethod() 的结果,而不是它被调用。

标签: javascript reactjs event-handling jasmine jestjs


【解决方案1】:

对于此类测试,常见的路径是在组件实例上调用处理程序方法,而不是模拟事件。

您可以确定 onClick 属性是由 React 团队测试的,您可以检查的是当“他们”调用您的处理程序时会发生什么。

这也允许您使用浅层渲染,至少对我来说要快得多。您可以使用包装器中的 instance() 方法轻松获取对实例的引用,然后您可以调用您的处理程序并监视您想要的任何内容。

const wrapper = mount(<MyComponent />)
const instance = wrapper.instance()
instance.clickHandler()
//assert whatever

事实上,可能将您的间谍附加到实例的方法(已经绑定)也可以解决问题。 :)

http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html

希望对你有帮助!

【讨论】:

  • (1) 如果你想演示浅渲染,你应该使用shallow...而不是mount...吗? (2) 我不知道.instance()。我(显然)使用.get(0) 实现了同样的事情,但我认为.instance() 更清晰,所以谢谢你。 (3) 我一定会考虑您对什么进行测试的建议,而不仅仅是如何。但是,我仍然想知道为什么没有调用component.clickHandler,即使它对我来说并不是很有价值,首先要检查它。 (4) 我不明白您将间谍附加到已绑定的实例方法的建议。
  • 另外,我不想演示什么,只是指出不需要完全安装,我更喜欢浅的方式。
  • 你说得对,getinstance 是不同的,所以我会更仔细地研究一下。然而,在这个例子中,它们确实完成了同样的事情。另外,我同意你的观点,如果可能的话,我更喜欢浅安装。但是,在这种情况下,无论是在我的代码还是您的代码中,如果您想访问组件的方法,无论它是像 changeHandler 这样的事件侦听器还是像 otherMethod 这样的更“标准”的方法,完全安装似乎是必要的。
  • 你好安德鲁。不,您也可以使用浅渲染调用实例方法。正如其他人在对您的问题的评论中指出的那样,您通常会向组件注入一个函数道具并从事件处理程序调用该道具(它可能是 Redux 操作创建者,父组件回调......)。要测试您是否会使用假道具(间谍)安装/浅化您的组件,然后像我在答案中显示的那样调用您的实例方法,并验证您的间谍(道具)已被调用。我已经以这种方式测试了数百个组件,当我无法测试时,通常会觉得我的组件有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 1970-01-01
  • 2021-12-25
  • 2012-02-14
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多