How can I spy on a class property arrow function using Jest? I have the following example test case which fails with the error Expected mock function to have been called.
:
import React, {Component} from "react";
import {shallow} from "enzyme";
class App extends Component {
onButtonClick = () => {
// Button click logic.
};
render() {
return <button onClick={this.onButtonClick} />;
}
}
describe("when button is clicked", () => {
it("should call onButtonClick", () => {
const app = shallow(<App />);
const onButtonClickSpy = jest.spyOn(app.instance(), "onButtonClick");
const button = app.find("button");
button.simulate("click");
expect(onButtonClickSpy).toHaveBeenCalled();
});
});
I can make the test pass by changing the button's onClick
prop to () => this.onButtonClick()
but would prefer not to change my component implementation just for the sake of tests.
Is there any way to make this test pass without changing the component implementation?
To spy on an exported function in jest, you need to import all named exports and provide that object to the jest. spyOn function. That would look like this: import * as moduleApi from '@module/api'; // Somewhere in your test case or test suite jest.
Jest can be used to mock ES6 classes that are imported into files you want to test. ES6 classes are constructor functions with some syntactic sugar. Therefore, any mock for an ES6 class must be a function or an actual ES6 class (which is, again, another function). So you can mock them using mock functions.
When mocking global object methods in Jest, the optimal way to do so is using the jest. spyOn() method. It takes the object and name of the method you want to mock, and returns a mock function. The resulting mock function can then be chained to a mocked implementation or a mocked return value.
According to this enzyme issue and this one, you have two options:
Option 1: Call wrapper.update()
after spyOn
In your case, that would be:
describe("when button is clicked", () => {
it("should call onButtonClick", () => {
const app = shallow(<App />);
const onButtonClickSpy = jest.spyOn(app.instance(), "onButtonClick");
// This should do the trick
app.update();
app.instance().forceUpdate();
const button = app.find("button");
button.simulate("click");
expect(onButtonClickSpy).toHaveBeenCalled();
});
});
Option 2: Don't use class property
So, for you, you would have to change your component to:
class App extends Component {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
// Button click logic.
};
render() {
return <button onClick={this.onButtonClick} />;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With