Testing component function in React with Enzyme and Jest - reactjs

I am testing a component following the Container/Presentational pattern. What I need in order to have my coverage at 100% is to test the handleChange() function in which I have a setState(). The thing is that I'm just testing the Container component and the function is called in Presentational one.
Container Component:
import React, { Component } from 'react'
import DataStructureFormView from './DataStructureFormView'
export default class DataStructureForm extends Component {
state = {}
handleChange = name => event => {
this.setState({
[name]: event.target.value
})
}
render() {
return (
<DataStructureFormView
handleChange={this.handleChange}
form={this.state}
/>
)
}
}
As you can see, the DataStructureForm is the Container component and DataStructureFormView is the Presentational one.
Test file:
import React from 'react'
import { shallow, mount } from 'enzyme'
describe('DataStructureForm', () => {
it('should call the handleChange() function and change the state', () => {
const component = mount(<DataStructureForm />)
const handleChange = jest.spyOn(component.instance(), 'handleChange')
component.instance().handleChange()
expect(handleChange).toBeCalled()
}
}
This is one of the multiple approaches that I have done, but it is not testing the setState() inside the handleChange() method.
What else I can do?

The most straightforward solution is to check if state changed accordingly:
const component = shallow(<DataStructureForm />)
const value = 'someValue';
component.instance().handleChange('someName')({target: { value }});
expect(component.state('someName')).toEqual(value)

Related

How can i test the state in react functional components?

I am using jest and enzyme to test my components. If i have the following class component
import React, { Component } from "react";
export class Test extends Component {
state = {
isLoading: false,
};
onClick = () => {
this.setState({ isLoading: true });
};
render() {
return (
<div>
<button onClick={this.onClick}>Click button</button>
</div>
);
}
}
export default Test;
then to write test for this i will use:
import {Test} from "../Test";
import { shallow } from "enzyme";
import React from "react";
it('default state value for isLoading should be false', () => {
const wrapped = shallow(<Test/>);
const initialIsLoading = wrapped.state('isLoading');
expect(initialIsLoading).toBe(false);
})
it('change isLoading true on button click', () => {
const wrapped = shallow(<Test/>);
const initialIsLoading = wrapped.state('isLoading');
console.log(initialIsLoading);
wrapped.find('button').simulate('click');
wrapped.update();
const newValue = wrapped.state('isLoading');
console.log(newValue);
expect(newValue).toEqual(true);
})
but if my component is functional then i get error - state cannot be used on functional components
when i run my tests.
How can i test my functional components state ?
shallow wrapper doesn't support hooks, which provide state to functional components. You can either use Enzyme's mount wrapper or switch to React Testing Library which is better than Enzyme for modern generation of React.
Please note that mount can render stateful functional components, but not able to access the state. These will fail:
mount(<Test/>).setState();
mount(<Test/>).state();

How to test HOC with enzyme, chai

I have a HOC function that receives a React component and returns that react component with two new method properties (handleBack & moveitOnTop) like so:
import React, { Component } from "react";
import classNames from "classnames";
export default WrappedComponent => {
return class extends Component {
constructor(props) {
super(props);
this.moveitOnTop = this.moveitOnTop.bind(this);
this.handleBack = this.handleBack.bind(this);
this.state = {
isSearchActive: false
};
}
moveitOnTop(flag) {
this.setState({ isSearchActive: flag });
window.scrollTo(0, -100);
}
handleBack() {
this.setState({ isSearchActive: false });
if (document.body.classList.contains("lock-position")) {
document.body.classList.remove("lock-position");
}
}
render() {
const props = {
...this.props,
isSearchActive: this.state.isSearchActive,
moveitOnTop: this.moveitOnTop,
goBack: this.handleBack
};
const classes = classNames({ "c-ftsOnTop": this.state.isSearchActive });
return (
<div className={classes}>
<WrappedComponent {...props} />
</div>
);
}
};
};
The component:
//import fireAnalytics
import { fireAnalytics } from "#modules/js-utils/lib";
class MyComponent extender Component{
constructor(){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
// calling analytics function by passing vals
fireAnalytics({
event: "GAEvent",
category: "",
action: `Clicked on the ${e.target.id} input`,
label: "Click"
});
// I CALL THE HOC PROPERTY
this.props.moveitOnTop(true);
// I CALL THE HOC PROPERTY
this.props.handleBack();
}
render(){
return(
<div className="emailBlock">
<input type="text" onClick={handleClick} />
<Button className="submit">Submit</Button>
</div>
)
}
}
// export HOC component
export default hoc(MyComponent);
// export just MyComponent
export {MyComponent};
I want to test the HOC:
I need to check that class .c-ftsOnTop exists
I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
I need to check if className emailBlock exists.
The test that I tried, but fails:
import { mount, shallow } from 'enzyme';
import sinon from 'sinon';
import React from 'react';
import { expect } from 'chai';
import hoc from '....';
import {MyComponent} from '...';
import MyComponent from '....';
it('renders component', () => {
const props = {}
const HocComponent = hoc(MyComponent);
const wrapper = mount(
<HocComponent {...props} />
);
console.log('wrapper:', wrapper);
expect(wrapper.find('.c-ftsOnTop')).to.have.lengthOf(1);
expect(wrapper.hasClass('c-fts-input-container')).toEqual(true);
})
Error
AssertionError: expected {} to have a length of 1 but got 0
console.log: wrapper: ReactWrapper {}
Can anybody help me on how to render the HOC?
Here is a working test:
import { mount } from 'enzyme';
import React from 'react';
import WrappedMyComponent from './MyComponent';
it('renders component', () => {
const props = {}
const moveitOnTopSpy = jest.spyOn(WrappedMyComponent.prototype, 'moveitOnTop');
const handleBackSpy = jest.spyOn(WrappedMyComponent.prototype, 'handleBack');
const wrapper = mount(
<WrappedMyComponent {...props} />
);
// 1. I need to check that class .c-ftsOnTop exists
wrapper.setState({ isSearchActive: true }); // <= set isSearchActive to true so .c-ftsOnTop is added
expect(wrapper.find('.c-ftsOnTop')).toHaveLength(1); // Success!
// 2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
window.scrollTo = jest.fn(); // mock window.scrollTo
wrapper.find('input').props().onClick();
expect(moveitOnTopSpy).toHaveBeenCalled(); // Success!
expect(window.scrollTo).toHaveBeenCalledWith(0, -100); // Success!
expect(handleBackSpy).toHaveBeenCalled(); // Success!
// 3. I need to check if className emailBlock exists
expect(wrapper.find('.emailBlock')).toHaveLength(1); // Success!
})
Details
.c-ftsOnTop is only added when isSearchActive is true so just set the state of the component so the class is added.
If you create your spies on the prototype methods for moveitOnTop and handleBack, then when the hoc creates its instance methods by binding them to this in the constructor, the instance methods will be bound to your spies.
window.scrollTo logs an error to the console by default in jsdom so you can mock it to avoid that error message and to verify that it was called with the expected arguments.
Note that the above test requires the following typos to be fixed in MyComponent:
extender should be extends
constructor should take a props argument
onClick should be bound to this.handleClick instead of just handleClick
handleClick should call this.props.goBack() instead of this.props.handleBack()
(I'm guessing MyComponent was just thrown together as an example of the actual component)

Ho to simulate 'this.props' inside React Component when using Enzyme for tests?

So, my goal is to reach the func cashedImageCheker which must return the string , based on recived from this.props -> imageFetchedURL value. And my problem is that I cannot understand how to put this value into the corresponding func cashedImageCheker of my component Banners.
I tried to use Enzyme .setProps({}) method to transfer mock props inside my testing Component, but for now I just gets undefined. Here is my test and component codes below.
Thank you for any help...
TEST FILE:
import React from 'react'
import { mount, shallow } from 'enzyme'
import Banners from '../'
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const { imageFetchedURL } = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
COMPONENT FILE:
import PropTypes from 'prop-types'
import React, { Component } from 'react'
class Banners extends Component {
cashedImageCheker = () => {
const { imageFetchedURL } = this.props
const imageFetchCached = imageFetchedURL && imageFetchedURL.length > 1
return imageFetchCached
}
render() {
const isLocalImgCached = this.cashedImageCheker()
return (
<div className='bannerWrap'>
{isLocalImgCached}
</div>
)
}
}
export default Banners
When testing your component, you can pass the props required by it while shallow mounting the component like
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL={'https://google.com/la-la-la/1.jpg'}/>)
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
Also the major problem in code test case is that cashedImageChecker doesn't return you an object but a value and hence you need to write
const imageFetchedURL = Component.instance().cashedImageCheker()
instead of
const { imageFetchedURL } = Component.instance().cashedImageCheker()
Doing the above change will allow you to setProps and test the component method too.
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
So, my goal is to reach the func cashedImageCheker which must return
the string , based on recived from this.props -> imageFetchedURL
value.
Based on this one way is this
import React from 'react';
import { shallow } from 'enzyme';
import Banners from '../'
describe('<Banners />', () => {
const url = 'https://google.com/la-la-la/1.jpg';
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL= url />)
const imageFetchedURL = Component.instance().cashedImageCheker();
expect(imageFetchedURL.length).toBe(33)
})
})
And my problem is that I cannot understand how to put this value into
the corresponding func cashedImageCheker of my component Banners.
For this query you must know a thing when something is connected to props then during testing you can pass it on as actual props as I have done it
<Banners imageFetchedURL= url />
I tried to use Enzyme .setProps({}) method to transfer mock props
inside my testing Component
This is absolutely correct way too. It is another way of doing so.
But you were getting undefined because you were destructuring the return value which had nothing to do in your case.

Shallow test React branch Jest and Enzyme

There are three things I want to figure out. For now I am using shallow render. I use Enzyme and Jest.
I am wondering how I can test the branches in my React component. I
would like to test both sides of the if-else statement (? :). And I
do not want to pull it out in an own function.
How can I check if this.props.myFuncFromProps(value) is been called
when the input changes?
What is the best practice to test mapStateToProps and
mapDispatchToProps?
Here is an example of how my component would look like:
import React from 'react';
import MyChildComponent from 'wherever'; // This component is an input field in this example
export class MyComponent extends React.Component {
render() {
const myFunc(value) {
this.props.myFuncFromProps(value);
}
return (
<div>
{ this.props.isTrue ?
<MyChildComponent
value={this.props.value}
onChange={(value) => myFunc(value)}
/>
: null
}
</div>
);
}
}
To test the different states just render your component with the different attributes and make a snapshot (note, that you have to check the first time the snapshot was created). To test event callback, you have to pass a spy function (jest.fn()) into to component and use simulate to call the event, then test that the spy was called.
describe('MyComponent', () => {
describe('with isTrue is true', () => {
let myComponent
let myFuncFromProps
beforeEach(() => {
myFuncFromProps = jest.fn()
myComponent = shallow(
<MyComponent isTrue myFuncFromProps={myFuncFromProps} />
)
})
it('renders correct', () => {
expect(myComponent).matchSnapshot()
})
it('onchange will call myFuncFromProps', () => {
myComponent
.find('MyChildComponent')
.simulate('onChange', 'someValue')
expect(myFuncFromProps).toHaveBeenCalledWith('someValue')
})
})
it('with isTrue is false it renders correct', () => {
const myComponent = shallow(<MyComponent />)
expect(myComponent).matchSnapshot()
})
})

Testing React Component with Jest/Enzyme

I am trying to use Enzyme's shallow wrapper to get the instance of my component and calling my class function over it. It shows me this error:
TypeError: tree.instance(...).onCampaignSelected is not a function
class ToolbarPage extends Component {
constructor(){
super();
this.onCampaignSelected = this.onCampaignSelected.bind(this);
this.state = {
item: null
}
}
onCampaignSelected (item) {
this.setState({item})
}
render () {
return (
<MyComponent onItemSelected={this.onCampaignSelected} />
)
}
}
function mapStateToProps(state){
buttons: state.toolbar.buttons
}
export default connect(mapStateToProps)(ToolbarPage);
My test case looks like this
import { shallow, mount } from 'enzyme';
import ToolbarPage from './ToolbarPage';
import configureStore from 'configureStore';
const store = configureStore();
const props = {
store,
isLoggedIn: false,
messageCounter: 0
}
describe('<ToolbarPage />', () => {
it('allows to select campaign', () => {
const tree = shallow(<ToolbarPage {...props}/>);
tree.instance().onCampaignSelected();
})
})
I also figured out that it is a wrapped component, so I won't get this function on the wrapped component. How do I access this function?
shallow does not render the full set of components with all of their properties & methods. It is intended for basic "did this thing render what I expected?" testing.
mount will give you everything and should allow you to test whatever you need. It is very useful for testing event handling & manipulating the state of components to test the interactions between components.

Resources