我正在尝试为一个简单的React组件编写一个简单的测试,并且我想使用Jest来确认我用 enzyme 模拟点击时已调用了一个函数。根据Jest文档,我应该能够使用spyOn做到这一点:spyOn

但是,当我尝试这样做时,我不断收到TypeError: Cannot read property '_isMockFunction' of undefined,这意味着我的 spy 未定义。我的代码如下所示:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {

  myClickFunc = () => {
      console.log('clickity clickcty')
  }
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro" onClick={this.myClickFunc}>
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

在我的测试文件中:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { shallow, mount, render } from 'enzyme'

describe('my sweet test', () => {
 it('clicks it', () => {
    const spy = jest.spyOn(App, 'myClickFunc')
    const app = shallow(<App />)
    const p = app.find('.App-intro')
    p.simulate('click')
    expect(spy).toHaveBeenCalled()
 })
})

有人了解我在做什么错吗?

最佳答案

除了spyOn的方式,您几乎完成了一切工作。
使用 spy 程序时,有两个选择:spyOnApp.prototype或组件component.instance()
const spy = jest.spyOn(Class.prototype,“method”)
将 spy 附加到类原型(prototype)上并渲染(浅渲染)实例的顺序很重要。

const spy = jest.spyOn(App.prototype, "myClickFn");
const instance = shallow(<App />);
第一行的App.prototype位是使工作正常运行所需要的。在使用class实例化JavaScript或将其浸入new MyClass()之前,JavaScript MyClass.prototype没有任何方法。对于您的特定问题,您只需要监视App.prototype方法myClickFn即可。
jest.spyOn(component.instance(),“方法”)
const component = shallow(<App />);
const spy = jest.spyOn(component.instance(), "myClickFn");
此方法需要shallow/render/mountReact.Component实例可用。本质上spyOn只是在寻找要劫持并插入jest.fn()的东西。它可能是:
一个简单的object:
const obj = {a: x => (true)};
const spy = jest.spyOn(obj, "a");
一个class:
class Foo {
    bar() {}
}

const nope = jest.spyOn(Foo, "bar");
// THROWS ERROR. Foo has no "bar" method.
// Only an instance of Foo has "bar".
const fooSpy = jest.spyOn(Foo.prototype, "bar");
// Any call to "bar" will trigger this spy; prototype or instance

const fooInstance = new Foo();
const fooInstanceSpy = jest.spyOn(fooInstance, "bar");
// Any call fooInstance makes to "bar" will trigger this spy.
React.Component instance:
const component = shallow(<App />);
/*
component.instance()
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(component.instance(), "myClickFn");
React.Component.prototype:
/*
App.prototype
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(App.prototype, "myClickFn");
// Any call to "myClickFn" from any instance of App will trigger this spy.
我已经使用并看到了两种方法。当我有一个beforeEach()beforeAll()块时,我可能会采用第一种方法。如果我只需要快速监视,我将使用第二个。只需注意附加 spy 的顺序即可。
编辑:
如果要检查myClickFn的副作用,可以在单独的测试中调用它。
const app = shallow(<App />);
app.instance().myClickFn()
/*
Now assert your function does what it is supposed to do...
eg.
expect(app.state("foo")).toEqual("bar");
*/
编辑:
这是使用功能组件的示例。请记住,功能组件范围内的所有方法均不可用于 spy Activity 。您将监视传递到功能组件中的功能 Prop 并测试它们的调用。本示例探讨了jest.fn()jest.spyOn相反的用法,两者均共享模拟功能API。尽管它没有回答原始问题,但仍然提供了其他技术的见解,这些技术可能适合与该问题间接相关的案例。
function Component({ myClickFn, items }) {
   const handleClick = (id) => {
       return () => myClickFn(id);
   };
   return (<>
       {items.map(({id, name}) => (
           <div key={id} onClick={handleClick(id)}>{name}</div>
       ))}
   </>);
}

const props = { myClickFn: jest.fn(), items: [/*...{id, name}*/] };
const component = render(<Component {...props} />);
// Do stuff to fire a click event
expect(props.myClickFn).toHaveBeenCalledWith(/*whatever*/);

09-28 06:00