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.
Related
I've been trying to test if a React function has been called on Click but i always get the same result :
Expected number of calls: >= 1
Received number of calls: 0
And the test fails, this is my Setup,
This is MyComponent
import React from 'react';
export default function MyComponent() {
function handleClick() {
console.log('Clicking');
}
return (
<div>
<button onClick={handleClick}>Click</button>
</div>
);
}
And here is my Jest Test
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
render(<MyComponent />);
const handleClick = jest.fn();
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});
This works normally on the app, it does call the function and logs 'Clicking'
Thanks in advance!
You have defined the mock function but you're Component doesn't know anything about it since it's defined within the component.
What you could do is pass the onClick as a prop to the Component. This way you could pass any handler function required for the Component as a prop.
import React from 'react';
export default function MyComponent({ onClick }) { // 👈 receive as a prop
/* function handleClick() {
console.log('Clicking');
} */
return (
<div>
<button onClick={onClick}>Click</button> {/* 👈 call the prop */}
</div>
);
}
Update test:
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
const handleClick = jest.fn();
render(<MyComponent onClick={handleClick} />); // 👈 pass the mock
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});
This is my test case
import React from 'react';
import { mount } from 'enzyme';
import CustomForm from '../index';
describe('Custom Form mount testing', () => {
let props;
let mountedCustomForm;
beforeEach(() => {
props = {
nav_module_id: 'null',
};
mountedCustomForm = undefined;
});
const customform = () => {
if (!mountedCustomForm) {
mountedCustomForm = mount(
<CustomForm {...props} />
);
}
return mountedCustomForm;
};
it('always renders a div', () => {
const divs = customform().find('div');
console.log(divs);
});
});
Whenever I run the test case using yarn test. It gives the following error TypeError: Cannot read property 'nav_module_id' of undefined.
I have already placed console at multiple places in order to see the value of props. It is getting set. But it couldn't just pass through the components and give the error mentioned above.
Any help would be appreciated been stuck for almost 2-3 days now.
You have to wrap the component that you want to test in beforeEach method such that it becomes available for all the 'it' blocks, and also you have to take the mocked props that you think you are getting into the original component.
import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'
import sinon from 'sinon'
import {Map} from 'immutable'
import {ClusterToggle} from '../../../src/MapView/components/ClusterToggle'
describe('component tests for ClusterToggle', () => {
let dummydata
let wrapper
let props
beforeEach(() => {
dummydata = {
showClusters: true,
toggleClustering: () => {}
}
wrapper = shallow(<ClusterToggle {...dummydata} />)
props = wrapper.props()
})
describe(`ClusterToggle component tests`, () => {
it(`1. makes sure that component exists`, () => {
expect(wrapper).to.exist
})
it('2. makes sure that cluster toggle comp has input and label', () => {
expect(wrapper.find('input').length).to.eql(1)
expect(wrapper.find('label').length).to.eql(1)
})
it('3. simulating onChange for the input element', () => {
const spy = sinon.spy()
const hct = sinon.spy()
hct(wrapper.props(), 'toggleClustering')
spy(wrapper.instance(), 'handleClusterToggle')
wrapper.find('input').simulate('change')
expect(spy.calledOnce).to.eql(true)
expect(hct.calledOnce).to.eql(true)
})
})
})
I'm using Enzyme/Jest to write a test for a function on my component that is triggered through an onClick of a material's menu icon. The function is passed as props to the component. When writing test, it gives me error on simulating click.
import React from 'react';
import scssstyles from './ToggleSideNavComponent.scss';
class ToggleSideNavComponent extends React.Component {
render() {
return (
<a
id="toggle_sidebar_btn"
className={scssstyles.menuIcon}
onClick={this.props.handleSideNavToggle}
>
<i id="menu" className="material-icons menu-icon" style={{fontSize:30}}>menu</i>
</a>
);
}
}
module.exports = ToggleSideNavComponent;
TEST
import expect from 'expect';
import React from 'react';
import {mount, shallow} from 'enzyme';
import ToggleSideNavComponent from './ToggleSideNavComponent';
import sinon from 'sinon';
function setup() {
const props = {
handleSideNavToggle: sinon.spy()
};
return {
props: props,
wrapper: shallow(<ToggleSideNavComponent {...props} />)
};
}
describe('ToggleSideNavComponent', () => {
it('should have menu icon', () => {
const component = setup();
expect(component.wrapper.find('a #menu').length).toBe(1);
component.wrapper.find('#toggle_sidebar_btn #menu').simulate('click');
expect(component.props.handleSideNavToggle).toHaveBeenCalled();
});
});
error :
ToggleSideNavComponent › should have menu icon
expect(jest.fn())[.not].toHaveBeenCalled()
jest.fn() value must be a mock function or spy.
Received:
function: [Function proxy]
at Object.<anonymous> (src/components/app/Header/NavLeftList/ToggleSideNavComponent/ToggleSideNavComponent.spec.js:25:94)
at new Promise (<anonymous>)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
If you're using Jest, you should use Jest's spies, rather than Sinon.
const props = {
handleSideNavToggle: jest.fn()
};
I've tried your code but changed shallow rendering to full DOM rendering using mount and everything works fine:
import React from 'react';
import {shallow, mount} from 'enzyme';
import ToggleSideNavComponent from './ToggleSideNavComponent';
function setup() {
const props = {
handleSideNavToggle: jest.fn()
};
return {
props: props,
wrapper: mount(<ToggleSideNavComponent {...props} />)
};
}
describe('ToggleSideNavComponent', () => {
it('should have menu icon', () => {
const component = setup();
expect(component.wrapper.find('a #menu').length).toBe(1);
component.wrapper.find('#toggle_sidebar_btn #menu').simulate('click');
expect(component.props.handleSideNavToggle).toHaveBeenCalled();
component.wrapper.unmount(); // don't forget to use unmount() after mounting
});
});
seems like there is a problem accessing props when using shallow rendering
If you only want to test the behavior of the handleSideNavToggle function (just unit test this function) then I don't recommend doing it using simulate because you don't need to test React's event wiring nor the browser's, instead you could have shallow rendered the parent component which provides this prop function to your component ToggleSideNavComponent and call this method using suitable arguments and assert results, for example:
describe('ToggleSideNavComponentParent', () => {
it('test parent component function', () => {
const wrapper = shallow(<ToggleSideNavComponentParent/>);
const instance = wrapper.instance();
let e = prepareSuitableEventObject();
instance.handleSideNavToggle(e);
// do your assertions here
});
});
function prepareSuitableEventObject() {
// construct an object and return it
}
I have a simple test:
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () => {
test('Button action called when clicked', () => {
var clicked = false;
const component = shallow(
<GenericButton action={() => {
clicked = true;
}}
id="testComponent"/>
);
component.find('testComponent').first().simulate('click');
expect(clicked).toBeTruthy();
});
});
However this will fail as the action is done eventually,
If i set the assertion to happen eventually (using setTimeout for example) it will work
however it would be better if i do something of the following before the assert (found this on examples using jasmine)
waitsFor(() => {
return clicked;
}, "the value to change", 1000);
What is the equivalent call for enzyme/jest?
Edit: Content of component
render() {
return <Button id={this.props.id}
key={this.props.id}
onClick={() => this.props.action()}
bsStyle={this.props.style}>
{this.props.text}
</Button>;
}
(Button is a 3rd party library component)
To test if a click handler was added correctly pass a spy into your comment, simulate the click and check that the spy was called. Doing this there is no need to use waitsFor.
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () = > {
test('Button action called when clicked', () = > {
const action = jest.fn();
const component = shallow(
<GenericButton action={action}
id="testComponent"/>
);
component.find('Button').first().simulate('click');
expect(action).toHaveBeenCalled();
});
});
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();
});