How to spy on a class property arrow function using Jest - reactjs

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?

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

Related

React jest enzyme function called test

Hi I have a simple component I need to test:
MyComponent.js-----
import React from 'react';
const MyComponent = (props) => {
onClickHandler = () => {
console.log('clicked');
props.outsideClickHandler();
}
return (
<div>
<span className='some-button' onClick={onClickHandler}></span>
</div>
);
}
MyComponent.test.js----
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
const onClickHandler = jest.fn();
it('calls click event', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1); // tried this first
expect(onClickHandler).toBeCalled(); // tried this next
});
});
Tried above two types of expect, my console log value is coming
console.log('clicked'); comes
but my test fails and I get this:
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
So, the problem with your code is when you simulate a click event, you expect a totally independent mock function to be called. You need to attach the mock function to the component. The best way is using prototype. Like this:
it('calls click event', () => {
MyComponent.prototype.onClickHandler = onClickHandler; // <-- add this line
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1);
expect(onClickHandler).toBeCalled();
expect(onClickHandler).toHaveBeenCalledTimes(1); // <-- try this as well
});
Refer to this issue for more potential solutions.

How can i test with Enzyme+Mocha method call inside Component

Cannot figure out how to test a component method with enzyme + mocha + sinon. I want to test if component calls method loadPosts on button click.
import React from 'react';
import { configure, mount} from 'enzyme';
import { expect } from 'chai';
import Adapter from 'enzyme-adapter-react-16';
import { Posts } from '../Components/Posts';
import sinon from 'sinon';
configure({ adapter: new Adapter() });
describe('Posts', () => {
let wrapper;
let inst;
beforeEach(() => {
wrapper = mount(<Posts />);
inst = wrapper.instance();
sinon.spy(inst, 'loadPosts');
wrapper.find('button').simulate('click');
});
it('should load posts on button click', () => {
wrapper.update();
expect(inst.loadPosts).to.have.property('callCount', 1);
});
it('should set `loading` to true', () => {
expect(wrapper.state('loading')).to.equal(true);
});
});
And here is my component:
import React, {Component} from 'react';
import axios from 'axios';
export class Posts extends Component {
state = {
posts: null,
loading: false
}
componentDidMount() {}
loadPosts = () => {
this.setState({loading: true}, () => {
axios.get('https://jsonplaceholder.typicode.com/todos')
.then( d => this.setState({
posts: d.data
}));
});
}
render() {
return (<div>
<h4>I am posts</h4>
<button onClick= {this.loadPosts}>Load posts</button>
</div>);
}
}
But my test fails with error: Exception error: expected [Function] to have property 'callCount' of 1 but got 0
The onClick of your button gets bound directly to whatever this.loadPosts is when the component renders.
When you replace loadPosts with the spy, it doesn't have any effect on the currently rendered button so onClick doesn't call your spy.
The two options for fixing it are to call this.loadPosts using an arrow function like this:
<button onClick={() => this.loadPosts()}>Load posts</button>
...so that when onClick gets called it calls whatever this.loadPosts is currently set to.
The other option is to force a re-render after you create your spy so onClick gets bound to your spy instead of the original function.

How to test function that passed from mapDispatchToProps (React/Redux/Enzyme/Jest)

I want to test that function passed from mapDispatchToProps was invoked when button clicking is simulated.
How to test that function which passed from mapDispatchToProps is invoked?
I tried to pass a mocked function by props, but it doesn't work. Any help will be appreciated.
Here below my fake class code and test example.
My component
// All required imports
class App extends React.Component<Props> {
render() {
const { onClick } = this.props;
return (
<>
<h1>Form</h1>
<input />
<button onClick={() => onClick()} />
</>
);
}
}
const mapStateToProps = (state) => {
return {
state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => dispatch(actions.onClick())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
My test file
import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16/build/index';
import jsdom from 'jsdom';
import React from 'react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ConnectedApp, { App } from './App';
function setUpDomEnvironment() {
const { JSDOM } = jsdom;
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'http://localhost/' });
const { window } = dom;
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
}
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
setUpDomEnvironment();
configure({ adapter: new Adapter() });
const mockStore = configureMockStore();
describe('App', () => {
describe('When App connected to store', () => {
describe('When button clicked', () => {
it('should not crush after click on login button', () => {
const onClick = jest.fn()
const store = mockStore(initialStates[1]);
const wrapper = mount(
<Provider store={store}>
<ConnectedApp />
</Provider>);
wrapper.find('button').simulate('click');
??? how to test that function passed from mapDispatchToProps was fired?
});
});
});
});
I recommend following the approach described in the docs and export the connected component as the default export for use in the application, and export the component itself as a named export for testing.
For the code above export the App class and test the click like this:
import * as React from 'react';
import { shallow } from 'enzyme';
import { App } from './code';
describe('App', () => {
it('should call props.onClick() when button is clicked', () => {
const onClick = jest.fn();
const wrapper = shallow(<App onClick={onClick} />);
wrapper.find('button').simulate('click');
expect(onClick).toHaveBeenCalledTimes(1);
});
});
shallow provides everything that is needed for testing the component itself. (shallow even calls React lifecycle methods as of Enzyme v3)
As you have found, to do a full rendering of the component requires a mock redux store and wrapping the component in a Provider. Besides adding a lot of complexity, this approach also ends up testing the mock store and all child components during the component unit tests.
I have found it much more effective to directly test the component, and to export and directly test mapStateToProps() and mapDispatchToProps() which is very easy since they should be pure functions.
The mapDispatchToProps() in the code above can be tested like this:
describe('mapDispatchToProps', () => {
it('should dispatch actions.onClick() when onClick() is called', () => {
const dispatch = jest.fn();
const props = mapDispatchToProps(dispatch);
props.onClick();
expect(dispatch).toHaveBeenCalledWith(actions.onClick());
});
});
This approach makes unit testing the component very simple since you can pass the component props directly, and makes it very simple to test that the component will be handed the correct props by the pure functions (or objects) passed to connect().
This ensures that the unit tests are simple and targeted. Testing that connect() and redux are working properly with the component and all of its child components in a full DOM rendering can be done in the e2e tests.

States undefined in unit testing

I'm trying a very simple shallow test for a component:
it('renders without crashing', () => {
shallow(<SampleComponent />);
});
In my sample component, I did a setState :
this.setState({myCurrentState: "InSample"});
Now somewhere in my return, I used that to output an element, say:
return ( <h1> this.state.myCurrentState </h1>)
When I try the above test, I get
TypeError: Cannot read property of undefined.
I know that I can pass in props to shallow, but I can't seem to figure how to do it with states. Is there a better way of doing it? Sorry I'm new to React and unit testing. thanks
Here you go
import React from 'react';
import { render } from 'enzyme';
import SampleComponent from './SampleComponent';
describe('< SampleComponent />', () => {
it('should render', () => {
const wrapper = shallow(<SampleComponent name="Example" />);
expect(wrapper).toHaveLength(1);
});
describe('check props', () => {
const wrapper = shallow(<SampleComponent name="Example" />);
console.log(warpper.instance().props); // you should see name='Example'
});
});
Please check out this link http://airbnb.io/enzyme/docs/api/
You can't call the state like this return ( <h1> this.state.myCurrentState </h1>) you have to put it into { } like
return ( <h1> {this.state.myCurrentState} </h1>)
If you still having a troubles watch this video
Use Something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
myCurrentState: null
};
// somewhere in your code in one of the method
this.setState({myCurrentState: "InSample"});
render() {
return (
<h1>{this.state.myCurrentState}</h1>
);
}
}

Test custom method on React component has been called, using Enzyme and Sinon

I want to check that when a button is clicked on my component, it calls the method I have created to handle the click. Here is my component:
import React, { PropTypes, Component } from 'react';
class Search extends Component {
constructor(){
super();
this.state = { inputValue: '' };
}
handleChange = (e) => {
this.setState({ inputValue: e.target.value });
}
handleSubmit = (e) => {
e.preventDefault();
return this.state.inputValue;
}
getValue = () => {
return this.state.inputValue;
}
render(){
return (
<form>
<label htmlFor="search">Search stuff:</label>
<input id="search" type="text" value={this.state.inputValue} onChange={this.handleChange} placeholder="Stuff" />
<button onClick={this.handleSubmit}>Search</button>
</form>
);
}
}
export default Search;
and here is my test
import React from 'react';
import { mount, shallow } from 'enzyme';
import Search from './index';
import sinon from 'sinon';
describe('Search button', () => {
it('calls handleSubmit', () => {
const shallowWrapper = shallow(<Search />);
const stub = sinon.stub(shallowWrapper.instance(), 'handleSubmit');
shallowWrapper.find('button').simulate('click', { preventDefault() {} });
stub.called.should.be.true();
});
});
The call called property comes back false. I have tried loads of variation on the syntax and I think maybe I'm just missing something fundamental. Any help would be greatly appreciated.
I'm relatively new to Sinon as well. I have generally been passing spy()s into component props, and checking those (though you can use stub() in the same way):
let methodSpy = sinon.spy(),
wrapper = shallow(<MyComponent someMethod={methodSpy} />)
wrapper.find('button').simulate('click')
methodSpy.called.should.equal(true)
I point this out because I think it's the most straightforward way to unit test components (testing internal methods can be problematic).
In your example, where you're trying to test internal methods of a component, this wouldn't work. I came across this issue, though, which should help you out. Try:
it('calls handleSubmit', () => {
const shallowWrapper = shallow(<Search />)
let compInstance = shallowWrapper.instance()
let handleSubmitStub = sinon.stub(compInstance, 'handleSubmit');
// Force the component and wrapper to update so that the stub is used
compInstance.forceUpdate()
shallowWrapper.update()
shallowWrapper.find('button').simulate('click', { preventDefault() {} });
handleSubmitStub.called.should.be.true();
});

Resources