Enzyme Mount Test Failing With Redux Store State Update
I have a simple component with a button, that when pressed fetches a comments JSON from placeholder API.
My enzyme test with mount() is failing, even though I can see that the state is updating in the CommentList component. My manual tests in the browser display the comments fine. My test with mount and a mock store passes. I can even see that 2 li elements are created if I debug or console.log in CommentList. Does it seem like the view is not being updated in mount after the redux state change?
Apologies for the amount of code below, I'm not sure which part is the culprit. The project can be cloned from https://github.com/Hyllesen/react-tdd
integration.test.js (failing test)
import React from "react";
import { mount } from "enzyme";
import Root from "Root";
import CommentList from "components/CommentList";
import moxios from "moxios";
beforeEach(() => {
moxios.install();
moxios.stubRequest("http://jsonplaceholder.typicode.com/comments", {
status: 200,
response: [{ name: "Fetched #1" }, { name: "Fetched #2" }]
});
});
it("can fetch a list of comments and display them", () => {
//Render entire app
const wrapped = mount(
<Root>
<CommentList />
</Root>
);
//Find fetchComments button and click it
wrapped.find(".fetch-comments").simulate("click");
wrapped.update();
//Expect to find a list of comments
expect(wrapped.find("li").length).toBe(2);
});
Root.js
import React from "react";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reducers from "reducers";
import reduxPromise from "redux-promise";
export default ({
children,
store = createStore(reducers, {}, applyMiddleware(reduxPromise))
}) => {
return <Provider store={store}>{children}</Provider>;
};
CommentList.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchComments } from "actions";
class CommentList extends Component {
renderComments() {
console.log(this.props.comments);
return this.props.comments.map(comment => <li key={comment}>{comment}</li>);
}
render() {
const comments = this.renderComments();
//This is actually creating 2 li elements in the test
console.log("render", comments);
return (
<div>
<button className="fetch-comments" onClick={this.props.fetchComments}>
Fetch comments
</button>
<ul>{comments}</ul>
</div>
);
}
}
function mapStateToProps(state) {
return { comments: state.comments };
}
export default connect(
mapStateToProps,
{ fetchComments }
)(CommentList);
actions/index.js
import { FETCH_COMMENTS } from "actions/types";
import axios from "axios";
export function fetchComments() {
const response = axios.get("http://jsonplaceholder.typicode.com/comments");
return {
type: FETCH_COMMENTS,
payload: response
};
}
reducers/comments.js
import { FETCH_COMMENTS } from "actions/types";
export default function(state = [], action) {
switch (action.type) {
case FETCH_COMMENTS:
const comments = action.payload.data.map(comment => comment.name);
return [...state, ...comments];
default:
return state;
}
}
reducers/index.js
import { combineReducers } from "redux";
import commentsReducer from "reducers/comments";
export default combineReducers({
comments: commentsReducer
});
CommentList.test.js (test passing, using mock store)
import React from "react";
import { mount } from "enzyme";
import Root from "Root";
import CommentList from "components/CommentList";
import createMockStore from "utils/createMockStore";
let wrapped, store;
beforeEach(() => {
const initialState = {
comments: ["Comment 1", "Comment 2"]
};
store = createMockStore(initialState);
wrapped = mount(
<Root store={store}>
<CommentList />
</Root>
);
});
afterEach(() => {
wrapped.unmount();
});
it("Creates one li per comment", () => {
expect(wrapped.find("li").length).toBe(2);
});
it("shows text for each comment", () => {
expect(wrapped.render().text()).toEqual("Fetch commentsComment 1Comment 2");
});
Answer
It looks like your problem is caused by your moxios request stubbing. I think you need to wait for the response to be returned before calling update()
on your wrapper
.
beforeEach(() => {
moxios.install()
})
it('can fetch a list of comments and display them', done => {
// Render entire app
const wrapped = mount(
<Root>
<CommentList />
</Root>
)
// Find fetchComments button and click it
wrapped.find('.fetch-comments').simulate('click')
moxios.wait(() => {
let request = moxios.requests.mostRecent()
request
.respondWith({
status: 200,
response: [{ name: 'Fetched #1' }, { name: 'Fetched #2' }]
})
.then(function () {
wrapped.update()
expect(wrapped.find('li').length).toBe(2)
done()
})
})
})
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM