3

I got a trouble with React-Redux connected component testing. I want to test this component with asynchronous actions, but in console I always getting an error... I use React-testing-library with jest for testing (not enzyme).

There is console error

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: [Function anonymous]
Received: [Function anonymous]

Number of calls: 1

  45 |   it('should dispatch an action on page first render', () => {
  46 |     expect(store.dispatch).toHaveBeenCalledTimes(1);
> 47 |     expect(store.dispatch).toHaveBeenCalledWith(fetchTop10());
     |                            ^
  48 |   });
  49 | });
  50 |<br /><br />

There is connected component "CardList.js"

import React, { Fragment, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
// Redux
import { connect } from 'react-redux';
// Actions
import { fetchTop10 } from '../../actions/fetchTitles';   // async api call with data response
// Utils
import CardItem from './CardItem';

const CardList = ({ fetchTop10, top10 }) => {
  useEffect(() => {
    (async () => {
      await fetchTop10();
    })();
  }, [fetchTop10]);

  const renderCards = () => {
    if (top10.length === 0) {   // This is for sceletion insted of spinner
      return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((el) => {
        return <CardItem key={el} />;
      });
    }

    return top10.map((title) => {   // Actual data from action API call
      return <CardItem key={title.id} title={title} />;
    });
  };

  return (
    <Fragment>
      <section id="section-top" className="section-top">
        <h3 className="section-top__heading">Week Bestlers</h3>
        <div className="top-cards">{renderCards()}</div>
        <Link to="/titles" className="section-top__btn btn-link">
          view more &rarr;
        </Link>
      </section>
    </Fragment>
  );
};

CardList.propTypes = {
  fetchTop10: PropTypes.func.isRequired,
  top10: PropTypes.array.isRequired,
};

const mapStateToProps = (state) => {
  return { top10: state.top10 };
};

export default connect(mapStateToProps, { fetchTop10 })(CardList);<br /><br />

Testing "CardList.test.js"

import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';  // All project components are in global Router
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';  // for provider store
// Component
import CardList from '../CardList';
// Objects
import { fakeTitle, fakeTitle2 } from '../../utils/UserTitleObjects'; // Just objects with data
import history from '../../../history';
import { fetchTop10 } from '../../../actions/fetchTitles';    // Action with API call

const mockStore = configureStore([thunk]);

describe('My Connected React-Redux Component', () => {
  let store, component;

  beforeEach(() => {
    store = mockStore({
      top10: [],
    });

    store.dispatch = jest.fn();

    component = render(
      <Provider store={store}>
        <Router history={history}>
          <CardList />
        </Router>
      </Provider>
    );
  });

  afterEach(cleanup);

  it('should render with given state from Redux store', () => {
    expect(component.baseElement.querySelectorAll('.sceletion').length).toBe(
      60
    );
  });

  it('should dispatch an action on page first render', () => {
    expect(store.dispatch).toHaveBeenCalledTimes(1);  // this is good  <--
    expect(store.dispatch).toHaveBeenCalledWith(fetchTop10());    // Here starts a problem
  });
});

1 Answer 1

2

You have to mock your action in this case, otherwise, your action would return another function which is pretty hard to assert.

I would suggest to simply mock your action module like this:

CardList.test.js


// ...
const mockStore = configureStore([thunk]);

jest.mock("../../../actions/fetchTitles", () => ({
  __esModule: true,
  fetchTop10: jest.fn(),
}));

// ...

Sign up to request clarification or add additional context in comments.

2 Comments

Can you help me again?😅 Have the same component as up here, but need to fetch data after fireevent. Firstly I did the same as you said to mock etc., but it didn't work. Secondly I tried to use async/await, but it says "use custom middleware for async actions". Mocking module doesn't work. I tried to search in Google and solve this problem for 3 hours, but got nothing... Cuz I am new at testing. Anyway thanks for listening me
Can you create a new question and mention your problem into that?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.