Ad

Enzyme Mount Test Failing With Redux Store State Update

- 1 answer

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

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()
      })
  })
})
Ad
source: stackoverflow.com
Ad