web-dev-qa-db-ja.com

呼び出されたJest spyOn関数

単純な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;

そして私のテストファイルで:

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

誰が私が間違っているのか洞察していますか?

35
Max Millington

ちょっとバディ私はここで少し遅れていることを知っていますが、あなたはspyOnの方法以外にほとんど変更なしでほぼ​​完了しました。スパイを使用する場合、2つのオプションがあります:spyOn the App.prototype、またはcomponent component.instance()

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

クラスプロトタイプにスパイをアタッチし、インスタンスをレンダリング(浅いレンダリング)する順序は重要です。

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

最初の行の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");

このメソッドを使用するには、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()ブロックがある場合、最初のアプローチを使用する可能性があります。クイックスパイが必要な場合は、2番目のスパイを使用します。スパイを取り付ける順序に注意してください。

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

あなたはほとんどそこにいます。そのための小道具の使用に関する@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()
 })
})

ドキュメント: http://airbnb.io/enzyme/docs/api/ShallowWrapper/instance.html

18
CharlieBrown

テストコードでは、AppをspyOn関数に渡そうとしていますが、spyOnはクラスではなくオブジェクトでのみ機能します。通常、ここで2つのアプローチのいずれかを使用する必要があります。

1)クリックハンドラーがプロップとして渡された関数を呼び出す場所。

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

これで、コンポーネントに小道具としてスパイ関数を渡し、呼び出されることをアサートできます。

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)クリックハンドラーがコンポーネントの状態を設定する場所。

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

これで、コンポーネントの状態についてアサーションを作成できます。

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