Jest spyOn函数调用

Max*_*ton 40 javascript testing reactjs jestjs enzyme

我正在尝试为一个简单的React组件编写一个简单的测试,我想使用Jest来确认当我用酶模拟点击时已经调用了一个函数.根据Jest文档,我应该可以使用spyOn这个:spyOn.

然而,当我尝试这个时,我不断得到TypeError: Cannot read property '_isMockFunction' of undefined我的意思,我的间谍是未定义的.我的代码看起来像这样:

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;
Run Code Online (Sandbox Code Playgroud)

在我的测试文件中:

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()
 })
})
Run Code Online (Sandbox Code Playgroud)

任何人都能洞察我做错了什么?

tay*_*ack 42

嘿伙计我知道我在这里有点晚了,但除了你以外,你几乎没有任何改变spyOn.当您使用的间谍,你有两个选择:spyOnApp.prototype或组件component.instance().

const spy = jest.spyOn(Class.prototype,"method")

将间谍附加到类原型上并渲染(浅渲染)实例的顺序非常重要.

const spy = jest.spyOn(App.prototype, "myClickFn");
const instance = shallow(<App />);
Run Code Online (Sandbox Code Playgroud)

App.prototype第一行的位是您需要的东西.一个javascript class没有任何方法,直到你实例化它new MyClass(),或者你浸入MyClass.prototype.对于您的特定问题,您只需要监视该App.prototype方法myClickFn.

jest.spyOn(component.instance(),"method")

const component = shallow(<App />);
const spy = jest.spyOn(component.instance(), "myClickFn");
Run Code Online (Sandbox Code Playgroud)

此方法需要a的shallow/render/mount实例React.Component可用.基本上spyOn只是寻找劫持和推入的东西jest.fn().它可能是:

平原object:

const obj = {a: x => (true)};
const spy = jest.spyOn(obj, "a");
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

或者React.Component instance:

const component = shallow(<App />);
/*
component.instance()
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(component.instance(), "myClickFn");
Run Code Online (Sandbox Code Playgroud)

或者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.
Run Code Online (Sandbox Code Playgroud)

我已经使用并看过这两种方法.当我有一个beforeEach()或一个beforeAll()块时,我可能会采用第一种方法.如果我只需要一个快速的间谍,我将使用第二个.请记住附加间谍的顺序.

编辑:如果你想检查你的副作用,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");
*/
Run Code Online (Sandbox Code Playgroud)


Cha*_*own 18

你快到了.虽然我同意@Alex Young关于为此使用道具的答案,但instance在尝试窥探该方法之前,您只需要引用它.

describe('my sweet test', () => {
 it('clicks it', () => {
    const app = shallow(<App />)
    const instance = app.instance()
    const spy = jest.spyOn(instance, 'myClickFunc')

    instance.forceUpdate();    

    const p = app.find('.App-intro')
    p.simulate('click')
    expect(spy).toHaveBeenCalled()
 })
})
Run Code Online (Sandbox Code Playgroud)

文档:http: //airbnb.io/enzyme/docs/api/ShallowWrapper/instance.html

  • 在调用模拟单击之前,调用forceUpdate将spy函数附加到实例:instance.forceUpdate() (4认同)

Ale*_*ung 11

在您的测试代码中,您尝试传递App给spyOn函数,但spyOn只能使用对象,而不是类.通常,您需要使用以下两种方法之一:

1)点击处理程序调用作为prop传递的函数,例如

class App extends Component {

  myClickFunc = () => {
      console.log('clickity clickcty');
      this.props.someCallback();
  }
  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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以将间谍函数作为prop传递给组件,并断言它被调用:

describe('my sweet test', () => {
 it('clicks it', () => {
    const spy = jest.fn();
    const app = shallow(<App someCallback={spy} />)
    const p = app.find('.App-intro')
    p.simulate('click')
    expect(spy).toHaveBeenCalled()
 })
})
Run Code Online (Sandbox Code Playgroud)

2)点击处理程序在组件上设置某些状态,例如

class App extends Component {
  state = {
      aProperty: 'first'
  }

  myClickFunc = () => {
      console.log('clickity clickcty');
      this.setState({
          aProperty: 'second'
      });
  }
  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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以对组件的状态进行断言,即

describe('my sweet test', () => {
 it('clicks it', () => {
    const app = shallow(<App />)
    const p = app.find('.App-intro')
    p.simulate('click')
    expect(app.state('aProperty')).toEqual('second');
 })
})
Run Code Online (Sandbox Code Playgroud)

  • 类是一个对象.`spyOn`与`ClassName.prototype`一起使用 (2认同)
  • 类不是对象。在经典的 OO 中,它是一个对象的蓝图,在 JavaScript 中,它是一个函数。`typeof (class A {}) === "function"` 当我们实例化一个类时,我们根据类的原型创建一个对象。类的原型是一个对象,如果我们愿意的话,我们可以监视方法。最终,根据我在您的回答下的评论,我们想要测试点击处理程序的效果,而不仅仅是它被调用。 (2认同)