web-dev-qa-db-fra.com

Jest spyOn fonction appelée

J'essaie d'écrire un test simple pour un composant React simple et je veux utiliser Jest pour confirmer qu'une fonction a été appelée lorsque je simule un clic avec une enzyme. Selon la documentation Jest, je devrais pouvoir utiliser spyOn pour faire ceci: spyOn .

Cependant, lorsque j'essaie, j'obtiens toujours TypeError: Cannot read property '_isMockFunction' of undefined, ce qui signifie que mon espion n'est pas défini. Mon code ressemble à ceci:

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;

et dans mon fichier de test:

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()
 })
})

Quelqu'un a un aperçu de ce que je fais mal?

35
Max Millington

Hé mon pote, je sais que je suis un peu en retard, mais tu avais presque fini sans aucun changement, à part comment tu as spyOn. Lorsque vous utilisez l'espion, vous avez deux options: spyOn the App.prototype, ou composant component.instance().

const spy = jest.spyOn (Class.prototype, "méthode")

L'ordre d'attachement de l'espion sur le prototype de classe et le rendu (rendu peu profond) de votre instance sont importants.

const spy = jest.spyOn(App.prototype, "myClickFn");
const instance = shallow(<App />);

Le bit App.prototype sur la première ligne contient tout ce dont vous aviez besoin pour que les choses fonctionnent. Un javascript class ne possède aucune de ses méthodes jusqu'à ce que vous l'instanciez avec new MyClass() ou que vous plongiez dans le MyClass.prototype. Pour votre question particulière, vous aviez juste besoin d'espionner la méthode App.prototype _ myClickFn.

jest.spyOn (composant.instance (), "méthode")

const component = shallow(<App />);
const spy = jest.spyOn(component.instance(), "myClickFn");

Cette méthode nécessite qu'une instance shallow/render/mount d'un React.Component soit disponible. Essentiellement, spyOn recherche simplement quelque chose à détourner et passe à une jest.fn(). Il pourrait être:

Un object plain:

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.

Ou un React.Component instance:

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

Ou un 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.

J'ai utilisé et vu les deux méthodes. Lorsque j’ai un bloc beforeEach() ou beforeAll(), je peux choisir la première approche. Si j'ai juste besoin d'un espion rapide, je vais utiliser le second. Fais attention à l'ordre d'attacher l'espion.

EDIT: Si vous voulez vérifier les effets secondaires de votre myClickFn, vous pouvez simplement l'invoquer dans un test séparé.

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");
*/
35
taystack

Tu y es presque. Bien que je sois d’accord avec la réponse de @Alex Young concernant l’utilisation d’accessoires pour cela, vous avez simplement besoin d’une référence à la instance avant de tenter d’espionner la méthode.

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()
 })
})

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

18
CharlieBrown

Dans votre code de test, vous essayez de passer App à la fonction spyOn, mais spyOn ne fonctionnera qu'avec des objets, pas des classes. Généralement, vous devez utiliser l'une des deux approches suivantes:

1) Où le gestionnaire de clics appelle une fonction transmise comme accessoire, par ex.

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>
    );
  }
}

Vous pouvez maintenant transmettre une fonction espion en tant qu'accessoire au composant et affirmer qu'elle s'appelle:

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()
 })
})

2) Lorsque le gestionnaire de clics définit un état sur le composant, par exemple.

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>
    );
  }
}

Vous pouvez maintenant faire des assertions sur l’état du composant, c.-à-d.

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');
 })
})
11
Alex Young