Jest spyOn 函数调用
Jest spyOn function called
我正在尝试为一个简单的 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()
})
})
有人知道我做错了什么吗?
在您的测试代码中,您正试图将 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>
);
}
}
您现在可以将间谍函数作为 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()
})
})
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');
})
})
你快到了。尽管我同意@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
除了 spyOn
之外,您几乎没有任何改变就完成了。
当您使用间谍时,您有两个选择:spyOn
App.prototype
或组件 component.instance()
.
const spy = jest.spyOn(Class.prototype, "方法")
将间谍附加到 class 原型和渲染(浅渲染)您的实例的顺序很重要。
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.Component
的 shallow/render/mount
实例可用。本质上 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()
块时,我可能会采用第一种方法。如果我只需要一个快速间谍,我会使用第二个。请注意附加间谍的顺序。
编辑:
如果您想检查 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");
*/
编辑:
这是使用功能组件的示例。请记住,功能组件范围内的任何方法都不可用于监视。你会监视传递给你的功能组件的功能道具并测试它们的调用。此示例探讨了 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(没有道具或参数),那么您可以使用 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')
我正在尝试为一个简单的 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()
})
})
有人知道我做错了什么吗?
在您的测试代码中,您正试图将 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>
);
}
}
您现在可以将间谍函数作为 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()
})
})
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');
})
})
你快到了。尽管我同意@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
除了 spyOn
之外,您几乎没有任何改变就完成了。
当您使用间谍时,您有两个选择:spyOn
App.prototype
或组件 component.instance()
.
const spy = jest.spyOn(Class.prototype, "方法")
将间谍附加到 class 原型和渲染(浅渲染)您的实例的顺序很重要。
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.Component
的 shallow/render/mount
实例可用。本质上 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()
块时,我可能会采用第一种方法。如果我只需要一个快速间谍,我会使用第二个。请注意附加间谍的顺序。
编辑:
如果您想检查 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");
*/
编辑:
这是使用功能组件的示例。请记住,功能组件范围内的任何方法都不可用于监视。你会监视传递给你的功能组件的功能道具并测试它们的调用。此示例探讨了 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(没有道具或参数),那么您可以使用 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')