How Can I test react redux component by enzyme? - reactjs

I have to do it a few simple React Enzyme tests. I want to check if component is rendered.
import React from 'react';
import { shallow } from 'enzyme';
import ConnSearch from './ConnSearch';
it('renders without errors', () => {
const component = shallow(<ConnSearch />);
console.log(component.debug());
});
I have results: Could not find "store" in the context of "Connect(ConnSearch)". Either wrap the root component in a , or pass a custom React context provider to a
nd the corresponding React context consumer to Connect(ConnSearch) in connect options.
My ConnSearch Component:
import React, { Component } from 'react';
import {fetchRoadDetails, fetchUserPoints} from "../../actions";
import {connect} from "react-redux";
import RoadTable from "../../components/RoadTable/RoadTable";
import RoadForm from "../../components/RoadTable/RoadForm";
import style from './ConnSearch.module.scss'
import {getPoints} from "../../reducers";
class ConnSearch extends Component {
constructor(props){
super(props);
this.state = {
};
}
componentDidMount() {
this.props.fetchUserPoints(this.props.userLogin);
}
render() {
return (
<div className={style.wrapper}>
<RoadForm />
<div className={style.tableWrapper} >
<RoadTable/>
</div>
</div>
);
}
}
const mapDispatchToProps=dispatch=>({
fetchRoadDetails:()=>dispatch(fetchRoadDetails()),
fetchUserPoints:(user)=>dispatch(fetchUserPoints(user)),
});
const mapStateToProps = (state) => {
return {
roads: state.road,
points:getPoints(state),
userLogin: state.userLogin,
};
};
export default connect(mapStateToProps,mapDispatchToProps)(ConnSearch);
How can I do this test ? I've never done that before.

Unfortunately, when I wrap it in a provider:
it('renders without errors', () => {
const component = shallow( <Provider store={store}><ConnSearch/></Provider>);
console.log(component.debug());
});
I got this:
console.log src/views/ConnectionSearch/ConnSearch.test.js:11
<ContextProvider value={{...}}>
<Connect(ConnSearch) />
</ContextProvider>
I want ConnSearch render structure.

Related

How to snapshot test connected component that wrapped with connected component?

I need to ask about testing React using enzyme and jest. The case is I have a redux connected component wrapped by also connected component.
wrapper.js
import React from "react";
import { connect } from "react-redux";
import { state, actions } from "src/services/common-store/user";
function ApplicationLayout(props) {
return (
<div>
<h1>{props.title}</h1>
<div id="content">
{props.children}
</div>
</div>
);
}
export default connect(state, actions)(ApplicationLayout);
User.js
import React from "react";
import { connect } from "react-redux";
import { state, actions } from "src/services/common-store/user";
import ApplicationLayout from './ApplicationLayout';
function User(props) {
return (
<ApplicationLayout>
<h1>{props.user.name}</h1>
<img src={props.user.img} />
</ApplicationLayout>
);
}
export default connect(state, actions)(User);
in my test file, I already provided Provider with redux mock store.
User.test.js
import React from "react";
import { mount } from "enzyme";
import configureMockStore from "redux-mock-store";
import { Provider } from "react-redux";
import toJson from "enzyme-to-json";
import thunk from "redux-thunk";
import User from "./User.js";
// Mocking Redux store
const mockStore = configureMockStore([thunk]);
const store = mockStore({ title: "Application Layout", user: { name: "andri", img: "https://ava.com/img.jpg" } });
const setup = () => {
return mount(
<Provider store={store}>
<User />
</Provider>
);
};
describe("Test Component", () => {
let wrapper;
beforeEach(() => {
wrapper = setup();
});
describe("Components rendering", () => {
it("Should render without error and match snapshot", () => {
expect(toJson(wrapper)).toMatchSnapshot();
});
});
});
but I keep getting error said that props like title and user are undefined TypeError: Cannot read property 'user' of undefined. what can I do to address this?

React + Redux - Call a method of a component wrapped for the Redux Provider

Whats'up,
I am trying to test some react components that uses redux.
The default behavior should load by a rest call a list of options in a select input. This call is on the method componentDidMount() in my component.
The component works fine, but I cannot simulate the same behavior in my tests.
I cannot call the method componentDidMount() from my instance wrapped by Provider.
Can anyone help me with this
import React from 'react'
import {expect} from 'chai'
import {mount, shallow} from 'enzyme'
import sinon from 'sinon'
import { reducer as formReducer } from 'redux-form'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import ConnectedComponent from '../../../src/components/Component'
describe('Component <Component />', () => {
let store = createStore(combineReducers({ form: formReducer }))
let wrapper = mount(<Provider store={store}><ConnectedComponent /></Provider>)
// this call does not works
wrapper.instance().componentDidMount()
it('should load select input on component mount', () => {
expect(wrapper.find('select option')).to.have.length(12)
})
})
I was able to do like the following :
import React from 'react';
import {connect} from "react-redux";
export class Mock extends React.Component {
constructor(props) {
super(props);
}
myMethod() {
return 123
}
render() {
return (
<div>Test</div>
)
}
}
Mock = connect()(Mock);
export default Mock;
Jest test snippet :
const wrapper = mount(
<Provider store={store}>
<Mock/>
</Provider>
)
let result = wrapper.find(Mock).children().instance().myMethod();
expect(result).toEqual(123);
hope that helps someone!

Jest / React / Redux - MapDispatchToProps Undefined

I am trying to learn React w/ Jest / Enzyme.
I have a component that receives 2 props -
loadTenantListAction,
filterTenantListAction,
These props are passed in via mapDispatchToProps -
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import {
loadTenantListAction,
filterTenantListAction,
} from '../../store/actions';
import TenantList from './TenantList';
const mapStateToProps = tenants => ({ tenants });
const mapDispatchToProps = {
loadTenantListAction,
filterTenantListAction,
};
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(TenantList)
);
I have declared propTypes in my component as such -
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class TenantList extends Component {
static propTypes = {
loadTenantListAction: PropTypes.func.isRequired,
filterTenantListAction: PropTypes.func.isRequired,
};
render() {
return <p>Foo</p>;
}
}
My unit test is failing now showing that these props are marked as required, but are undefined. I expect this, as I am not passing them into my test -
import React from 'react';
import { shallow } from 'enzyme';
import TenantListContainer from '../../../src/containers/TenantList';
import TenantList from '../../../src/containers/TenantList/TenantList';
describe('<TenantList />', () => {
it('should render the TenantList Component', () => {
const wrapper = shallow(<TenantListContainer />);
expect(wrapper.find(<TenantList />)).toBeTruthy();
});
});
I can pass the test doing something like
expect(
wrapper.find(
<TenantList
loadTenantListAction={() => {}}
filterTenantListAction={() => {}}
/>
)
).toBeTruthy();
But that does not seem right at all, nor do I expect to be able to write useful tests by carrying on like that.
How should I be handling props passed in via mapDispatchToProps?
You can pass props directly to your component in shallow method.
describe('<TenantList />', () => {
const props = {
loadTenantListAction: () => {}, // or you can use any spy if you want to check if it's called or not
filterTenantListAction () => {},
}
it('should render the TenantList Component', () => {
const wrapper = shallow(<TenantListContainer {...props} />);
expect(wrapper.find(<TenantList />)).toBeTruthy();
});
});

TypeError: dispatch is not a function when testing with react-create-app jest and enzyme

I'm trying to setup testing on a new project created with react-create-app. Which now seems to be using React 16 and Jest 3 (which supposedly had some breaking changes, or maybe that was enzime). I'm getting an error similar to this post TypeError: dispatch is not a function when I try to test a method using JEST
TypeError: dispatch is not a function
at App.componentDidMount (src/components/App.js:21:68)
import React from 'react';
import { Provider } from 'react-redux';
import { mount } from 'enzyme';
import { App } from '../components/App';
import configureStore from '../state/store/configureStore';
window.store = configureStore({
slider: {
mainImageIndex: 0,
pageNum: 1,
perPage: 4,
},
});
const appTest = (
<Provider store={window.store}>
<App />
</Provider>
);
describe('App', () => {
it('should render without crashing', () => {
mount(appTest);
});
});
Originally I just tried to do this:
import React from 'react';
import { mount } from 'enzyme';
import { App } from '../components/App';
describe('App', () => {
it('should render without crashing', () => {
mount(<App />);
});
});
Which threw this error
Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(SearchForm))". Either wrap the root component in a , or explicitly pass "store" as a prop
Code for App.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchPhotos } from '../state/actions/searchPhotos';
import { setMainImageIndex, setFirstPage } from '../state/actions/slider';
import Slider from './Slider';
import SearchForm from './SearchForm';
import Error from './Error';
import '../styles/App.css';
export class App extends Component {
componentDidMount() {
const { dispatch } = this.props;
dispatch(searchPhotos(window.store));
}
searchPhotosSubmit = () => {
const { dispatch } = this.props;
dispatch(setFirstPage());
dispatch(setMainImageIndex(0));
dispatch(searchPhotos(window.store));
}
render() {
const { fetchError } = this.props;
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Flickr Slider in React.js</h1>
<SearchForm onSubmit={this.searchPhotosSubmit} />
</header>
{!fetchError ? <Slider /> : <Error />}
</div>
);
}
}
export default connect(state => ({
fetchError: state.fetchError,
form: state.form,
slider: state.slider,
}))(App);
Please not that you export both presentational component (as named export) and container component (as default export) in App.js. Then in your tests you import and use the presentational component using:
import { App } from '../components/App';
but you should import connected container component instead using:
import App from '../components/App'; // IMPORTANT! - no braces around `App`
Since you're using component that is not connected to Redux store dispatch prop is not injected as prop. Just use correct import and it should work.
For more details about importing default and named exports please check this doc. About presentational and container components you can read here.

enzyme shallow rendering just one node in redux components

Enzyme shallow rendering behaves in an unexpected way if I am rendering a redux component with a mocked store.
I have a simple test that looks like this :
import React from 'react';
import { shallow } from 'enzyme';
import { createMockStore } from 'redux-test-utils';
import Test from './Test'
it('should render ', () => {
const testState = {
app: {
bar: ['a', 'b', 'c']
}
};
const store = createMockStore(testState)
const context = {
store,
};
const shallowComponent = shallow(<Test items={[]}/>, {context});
console.log(shallowComponent.debug());
}
The Test component looks like :
class Test extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className="here"/>
)
}
}
export default Test;
Which as expected prints out this :
<div className="here" />
However if my component is a redux component :
class Test extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className="here"/>
)
}
}
const mapStateToProps = state => {
return {
barData: state.app.bar
}
}
export default connect(
mapStateToProps
)(Test)
Then what I get in the console is this :
<BarSeriesListTest items={{...}} barData={{...}} dispatch={[Function]} />
Why is there this difference? How do I test that my component has <div className="here"/> embedded in it in my redux version of the component?
You are referencing the HOC that connect is returning and not the component that you want to test.
You should use enzyme's dive function which will render the child component and return it as a wrapper.
const shallowComponent = shallow(<Test items={[]}/>, {context}).dive();
You can use it multiple times if you have multiple components that you need to dive through to get to. It's better than using mount as well because we are still testing in isolation.
You should export the unconnected component and test it separately (notice the first export):
export class Test extends React.Component {
}
...
export default connect(
mapStateToProps
)(Test)
While in your test you should test the rendering of the unconnected component like so (notice the curly braces around { Test }):
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import { Test } from './Test';
describe('...', () => {
it('...', () => {
const wrapper = shallow(<Test />)
expect(toJson(wrapper)).toMatchSnapshot();
})
})
Hope this helps.
Mode specifically for your described case the component should be:
import React from 'react';
import { connect } from 'react-redux';
export class Test extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className="here"/>
)
}
}
const mapStateToProps = state => {
return {
barData: state.app.bar
}
}
export default connect(
mapStateToProps
)(Test)
The test spec should be:
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import { Test } from 'Test';
describe('Test component', () => {
it('renders', () => {
const wrapper = shallow(<Test />);
expect(toJson(wrapper)).toMatchSnapshot();
});
});
Which generates the following snapshot:
exports[`Test component renders 1`] = `
<div
className="here"
/>
`;
You are exporting the connected component by default. What you can do is import the component that is not connected to redux.
import { Test } from './Test';
Then your test should work.

Resources