Testing react component with enzyme and expect fails - reactjs

I am testing a React component with Enzyme, Mocha, and Expect. The test case is shown below:
import React from 'react';
import expect from 'expect';
import { shallow } from 'enzyme';
import Add from '../src/client/components/add.jsx';
describe('Add', () => {
let add;
let onAdd;
before(() => {
onAdd = expect.createSpy();
add = shallow(<Add onAdd={onAdd} />);
});
it('Add requires onAdd prop', () => {
console.log(add.props());
expect(add.props().onAdd).toExist();
});
});
I am creating a spy using expect and attaching it to the onAdd prop of the Add component. My test checks if the prop exists on the component. For some reason, onAdd is undefined and the test fails. Any help?

The problem is that add isn't wrapping the <Add> component, it wraps what it returns. So, if your component looks like:
class Add extends React.Component {
render() {
return (
<div>
{this.props.foo}
</div>
);
}
}
This statement add.props().onAdd will try to access onAdd prop from <div> not from <Add>, and obviously it will fail.
This assertion:
expect(add.props().onAdd).toExist();
Will succeed, in the component will look like:
class Add extends React.Component {
render() {
return (
<div onAdd={this.props.onAdd}>
{this.props.foo}
</div>
);
}
}
Example shown in enzyme docs, is a little bit confusing.

Related

Testing component function in React with Enzyme and Jest

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)

Passing property dynamically to react component doesn't recognized by karma test

I'm trying to test whether a property that pass to a react component get the correct value, but it doesn't seems to work because (I think) the property get it's value dynamically depends on the parent component state. Here's the code (summarized):
import React from 'react';
import InfiniteScroll from 'react-infinite-scroller';
class myClass extends React.Component {
constructor(props) {
super(props);
this.state = {
....
inputChanged: 0
....
};
...
render() {
let items = [];
... code that retrieve items ...
return (
<InfiniteScroll
pageStart={this.state.inputChanged ? 1 : 0} // page start is changed according to this.state.inputChanged value
loadMore={this.loadMore.bind(this)}
hasMore={this.state.hasMoreItems}
loader={<div className="loader" key={0}>Loading ...</div>}
threshold={200}
>
<div className="container-fluid">
<div className="row">
{items}
</div>
</div>
</InfiniteScroll>
);
}
}
And in my spec file:
import React from 'react';
import {mount} from 'enzyme';
import {expect} from 'chai';
describe('myClass', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(
<myClass/>,
{attachTo: document.createElement('div')}
);
});
it('set correct pageStart', done => {
const InfiniteScroll = wrapper.find('InfiniteScroll');
wrapper.setState({
inputChanged: 1
});
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});
}
But no matter what, InfinteScroll.props().pageStart is 0 and the test fails.
As far as I understand, it should changed accounring to wrapper.state().inputChanged, but it doesn't. Any ideas why?
Thanks in advance!
You are holding on to the reference of InfiniteScroll before calling setState. You need to move the code to get the reference of InfiniteScroll after the setState. Here is the updated test.
it('set correct pageStart', done => {
wrapper.setState({
inputChanged: 1
});
const InfiniteScroll = wrapper.find('InfiniteScroll');
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});

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

Prevent enzyme shallow rendering on private component

How do I prevent shallow rendering on private component with enzyme?
Here is a component example:
// foo.jsx
import React from 'react';
// Private component
const FooSubtitle = ({subtitle}) => {
if (!subtitle) return null;
return <div className="foo__subtitle">{subtitle}</div>;
};
// Public component
const Foo = ({title, subtitle}) => (
<div className="foo">
<div className="foo__title">{title}</div>
<FooSubtitle subtitle={subtitle} />
</div>
);
export default Foo;
Here is my specification:
// foo.spec.js
import React from 'react';
import {shallow} from 'enzyme';
import Foo from './foo.jsx';
describe('Foo', () => {
it('should render a subtitle', () => {
const wrapper = shallow(<Foo title="my title" subtitle="my subtitle" />);
// This test doesn't work, so I cannot test the render of my component
expect(wrapper.find('.foo__subtitle').length).toBe(1);
// This one works, but it is not relevant
expect(wrapper.find('FooSubtitle').length).toBe(1);
});
});
Any idea?
Thanks a lot.
Shallow rendering is useful to constrain yourself to testing a
component as a unit, and to ensure that your tests aren't indirectly
asserting on behavior of child components.
I think you are trying to do what shallow tries to avoid ^^.
You can unit test the private component directly or use render :
expect(wrapper.find(Foo).render().find('.foo__subtitle')).to.have.length(1);
as explaned here : https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/render.md
But in both cases you'll need to export your Component and I must admit I had an error testing it with your component. :/
In this case (and in generally) your private component is just a function, use it as a function in the render of your public component and you will be able to test its render with the shallow wrapper.
<div className="foo">
<div className="foo__title">{title}</div>
{FooSubtitle({subtitle})}
</div>
Otherwise, I'm not sure it's a good idea to have complex private components...
You have to export your private component,
export const FooSubtitle = ...
Now, you are able to test it apart with all its prop variants.
Then you can test the presence of FooSubtitle, with particular props, in the render of Foo component as usual and nothing more.
If you have private components, and you want to test their implementation, you should:
Have at least enzyme v2.5
Look at the Enzyme .dive() API: Shallow render a non-DOM child of the current wrapper
Here is a working example:
// foo.jsx
import React from 'react';
// Private component
const FooSubtitle = ({subtitle}) => {
if (!subtitle) return null;
return <div className="foo__subtitle">{subtitle}</div>;
};
// Public component
const Foo = ({title, subtitle}) => (
<div className="foo">
<div className="foo__title">{title}</div>
<FooSubtitle subtitle={subtitle} />
</div>
);
export default Foo;
// foo.spec.js
import React from 'react';
import {shallow} from 'enzyme';
import Foo from './foo.jsx';
describe('Foo', () => {
it('should render a subtitle', () => {
const wrapper = shallow(<Foo title="my title" subtitle="my subtitle" />);
// This test works, but it is not relevant
expect(wrapper.find('FooSubtitle').length).toBe(1);
// This one need the `dive()` API to work
expect(wrapper.find('FooSubtitle').dive().find('.foo__subtitle').length).toBe(1);
});
});

React: onChange is not a function

Am new to react and am trying to run unit test for a react app using jest,
The app using the Flux pattern and I have already wrote one component "WEATHER" and before move on to another one I wanted to write test for that component and take TDD approach.
My code running fine but the test fail with this error
TypeError: tree.props.onChange is not a function
The weather component code :
// #flow
import React from 'react';
import api from '../api';
import weatherStore from '../stores/weatherStore';
//read from weather store
let _getState = () => {
return {weather: weatherStore.returnWeather()};
};
export default class Weather extends React.Component {
constructor(proporties) {
super(proporties);
this._onChange = this._onChange.bind(this);
this.state = _getState();
}
componentWillUnmount() {
weatherStore.removeListener('change', this._onChange);
}
componentDidMount() {
api.getWeather();
weatherStore.on('change', this._onChange);
}
_onChange() {
this.setState(_getState());
}
render() {
let weatherState = this.state.weather.map(weather => {
return <div key={weather.id} className="pull-right row">
<div>
<span>{weather.main.temp} C</span><br />
</div>
</div>;
})
return <div>{weatherState}</div>;
}
}
Where the test code:
import React from 'react';
import Weather from '../js/components/Weather';
import renderer from 'react-test-renderer';
it('should render weather temp and city', ()=> {
const component = renderer.create(
<Weather />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props._onChange();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Note since am using flow static type checker it always high light this line of code this._onChange = this._onChange.bind(this);
with error message saying : property '_onChange' property not found in the weather.
TypeError: tree.props.onChange is not a function
Have you tried updating your test with:
// manually trigger the callback
tree.props._onChange();
Note the dash.

Resources