调用了Jest spyOn函数

x8goxv8g  于 2023-11-15  发布在  Jest
关注(0)|答案(4)|浏览(167)

我正在尝试为一个简单的React组件编写一个简单的测试,我想使用Jest来确认当我用enzyme模拟点击时是否调用了一个函数。根据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()
 })
})


有人知道我哪里做错了吗?

plupiseo

plupiseo1#

你几乎没有做任何改变,除了你如何spyOn .当你使用间谍,你有两个选项:spyOnApp.prototype,或组件component.instance() .

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

将spy附加到类原型上并呈现(浅呈现)示例的顺序很重要。

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(),“方法”)

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


这个方法需要一个React.Componentshallow/render/mount示例可用。本质上,spyOn只是在寻找一些东西来劫持和塞进jest.fn()。它可能是:
一个普通的object

const obj = {a: x => (true)};
const spy = jest.spyOn(obj, "a");


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()块时,我可能会使用第一种方法。如果我只是需要一个快速的间谍,我会使用第二种方法。只要注意附加间谍的顺序。
编辑:如果你想检查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");
*/


编辑:下面是一个使用函数组件的例子。请记住,任何作用于函数组件的方法都不可用于监视。您将监视传入函数组件的函数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*/);


如果一个函数组件是niladic(没有props或arguments),那么你可以使用Jest来监视你期望从click方法中得到的任何效果:

import { myAction } from 'src/myActions'
function MyComponent() {
    const dispatch = useDispatch()
    const handleClick = (e) => dispatch(myAction('foobar'))
    return <button onClick={handleClick}>do it</button>
}

// Testing:
const { myAction } = require('src/myActions') // Grab effect actions or whatever file handles the effects.
jest.mock('src/myActions') // Mock the import

// Do the click
expect(myAction).toHaveBeenCalledWith('foobar')

mwkjh3gx

mwkjh3gx2#

虽然我同意@Alex Young关于使用props的回答,但在尝试窥探方法之前,您只需要引用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

7uzetpgm

7uzetpgm3#

在你的测试代码中,你试图将App传递给spyOn函数,但spyOn只对对象有效,而不是类。通常你需要使用以下两种方法之一:
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>
    );
  }
}

字符串
现在你可以将一个spy函数作为prop传递给组件,并Assert它被调用了:

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


您现在可以对组件的状态进行Assert,即:

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

li9yvcax

li9yvcax4#

看起来最佳的解决方案是避免测试这样的项目,而是测试导入是否使用正确的参数正确调用,或者是否显示了现在应该显示的内容(参见other answer的示例)。
但我仍然会添加一个替代方案来实际“修复”这个问题。在我的情况下,我使用了一个函数组件,所以其他答案都不起作用。我必须做的是将文件导入到它本身,并以这种方式调用有问题的函数。

// App.js
import * as thisModule from './App';

myClickFunc = () => {}

...
<p className="App-intro" onClick={thisModule.myClickFunc}>
  To get started, edit <code>src/App.js</code> and save to reload.
</p>

字符串
这是一个奇怪的问题(来源1,来源2)

相关问题