I'm trying to write a simple test for a simple React component, and I want to use Jest to confirm that a function has been called when I simulate a click with enzyme. According to the Jest docs, I should be able to use spyOn
to do this: spyOn.
However, when I try this, I keep getting TypeError: Cannot read property '_isMockFunction' of undefined
which I take to mean that my spy is undefined. My code looks like this:
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;
and in my test file:
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()
})
})
Anyone have an insight into what I'm doing wrong?
Best Answer
You were almost done without any changes besides how you
spyOn
. When you use the spy, you have two options:spyOn
theApp.prototype
, or componentcomponent.instance()
.const spy = jest.spyOn(Class.prototype, "method")
The order of attaching the spy on the class prototype and rendering (shallow rendering) your instance is important.
The
App.prototype
bit on the first line there are what you needed to make things work. A JavaScriptclass
doesn't have any of its methods until you instantiate it withnew MyClass()
, or you dip into theMyClass.prototype
. For your particular question, you just needed to spy on theApp.prototype
methodmyClickFn
.jest.spyOn(component.instance(), "method")
This method requires a
shallow/render/mount
instance of aReact.Component
to be available. EssentiallyspyOn
is just looking for something to hijack and shove into ajest.fn()
. It could be:A plain
object
:A
class
:Or a
React.Component instance
:Or a
React.Component.prototype
:I've used and seen both methods. When I have a
beforeEach()
orbeforeAll()
block, I might go with the first approach. If I just need a quick spy, I'll use the second. Just mind the order of attaching the spy.EDIT: If you want to check the side effects of your
myClickFn
you can just invoke it in a separate test.EDIT: Here is an example of using a functional component. Keep in mind that any methods scoped within your functional component are not available for spying. You would be spying on function props passed into your functional component and testing the invocation of those. This example explores the use of
jest.fn()
as opposed tojest.spyOn
, both of which share the mock function API. While it does not answer the original question, it still provides insight on other techniques that could suit cases indirectly related to the question.