Testing with jest+enzyme, mock functions is called twice for no reason

问题: I'm trying to learn Jest and Enzyme but I'm having a problem I can't find a solution to, this is my test.. it's not very good I know but I'm learning: import * as apiMock...

问题:

I'm trying to learn Jest and Enzyme but I'm having a problem I can't find a solution to, this is my test.. it's not very good I know but I'm learning:

import * as apiMock from '../api';

const fakePostId = '1';
const fakePersona = 'Fake';

jest.mock('../api', () => {
    return {
        fetchAllComments: jest.fn(() => {
            return [];
        }),
        filterComments: jest.fn(() => {
            return [];
        }),
        createCommentObject: jest.fn(() => {
            return [];
        }),
    };
});

test('checks if functions are called after didMount', () => {
    const component = shallow(
        <Comments postId={fakePostId} currentPersona={fakePersona} />
    );

    const spySetComments = jest.spyOn(
        component.instance(),
        'setCommentsFromLocalStorage'
    );

    component.instance().componentDidMount();

    expect(spySetComments).toHaveBeenCalledTimes(1);

    //Don't know why these are called 2! times, I can't see why removing componentDidMount makes it 0.
    expect(apiMock.fetchAllComments).toHaveBeenCalledTimes(1);
    expect(apiMock.filterComments).toHaveBeenCalledTimes(1);
}

The problem is toHaveBeenCalledTimes(1) fails with reason:

Expected mock function to have been called one time, but it was called two times.

But I don't know why. setCommentsFromLocalStorage only runs once, and that is the function that sohuld run from componentDidMount and execute these api calls once.

ReactComponent looks like this:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import CreateNewComment from './CreateNewComment';
import SingleComment from './SingleComment';
import * as api from '../api';

class Comments extends Component {
  state = {
    comments: []
  };

  componentDidMount() {
    this.setCommentsFromLocalStorage();
  }

  setCommentsFromLocalStorage = (postId = this.props.postId) => {
    const fetchedComments = api.fetchAllComments();
    const comments = api.filterComments(fetchedComments, postId);
    this.setState({ comments });
  };

  removeComment = commentId => {
    api.removeComment(commentId);
    this.setCommentsFromLocalStorage();
  };

  renderCommentList = (comments, currentPersona) =>
    comments.map(comment => (
      <SingleComment
        {...comment}
        currentPersona={currentPersona}
        key={comment.id}
        onClick={this.removeComment}
      />
    ));

  render() {
    return (
      <div className="py-2">
        <h2 className="text-indigo-darker border-b mb-4">Comments</h2>
        {this.renderCommentList(this.state.comments, this.props.currentPersona)}
        <CreateNewComment
          postId={this.props.postId}
          author={this.props.currentPersona}
          updateComments={this.setCommentsFromLocalStorage}
        />
      </div>
    );
  }
}

Comments.propTypes = {
  postId: PropTypes.string.isRequired,
  currentPersona: PropTypes.string.isRequired
};

export default Comments;

回答1:

componentDidMount gets called during shallow().


This means that setCommentsFromLocalStorage gets called which calls fetchAllComments and filterComments all during that initial shallow() call.

api has already been mocked so it records those calls to fetchAllComments and filterComments.


Once that has all happened, the spy is created for setCommentsFromLocalStorage and componentDidMount gets called again (which calls fetchAllComments and filterComments again).

The spy for setCommentsFromLocalStorage then correctly reports that it was called once (since it only existed during the second call to componentDidMount).

The spies on fetchAllComments and filterComments then correctly report that they were called two times since they existed during both calls to componentDidMount.


The easiest way to fix the test is to clear the mocks on fetchAllComments and filterComments before the call to componentDidMount:

apiMock.fetchAllComments.mockClear();  // clear the mock
apiMock.filterComments.mockClear();  // clear the mock

component.instance().componentDidMount();

expect(spySetComments).toHaveBeenCalledTimes(1);  // SUCCESS
expect(apiMock.fetchAllComments).toHaveBeenCalledTimes(1);  // SUCCESS
expect(apiMock.filterComments).toHaveBeenCalledTimes(1);  // SUCCESS

回答2:

Use beforeEach and afterEach to mock and mockRestore the spies, respectively.

This is explained in the Setup and Teardown section in Jest docs.

  • 发表于 2019-02-15 03:46
  • 阅读 ( 197 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除