Home > Net >  Assert that mapDispatchToProps function has been called
Assert that mapDispatchToProps function has been called

Time:01-21

I am attempting to test that I am calling one of my mapDispatchToProps functions with args, however I can't seem to get it to work...

I attempted to follow this previous question, but it didn't seem to work for me.

Component.jsx

const mapDispatchToProps = dispatch => ({
    myFunction: (data) => dispatch(myAction(data))
});

const Component = ({buttonText, myFunction}) => (
    <button data-testid="test" onClick={() => myFunction(123)}>{buttonText}</button>
)

export default connect(null, mapDispatchToProps)(Component);

Actions.js

export const myAction = agentData => ({
    type: `MY_ACTION`, 
    agentData
});

Test.js

import createMockStore from "redux-mock-store";

it('Should pass', () => {
    const mockStore = createMockStore();
    const store = mockStore({});

    const mockUpdate = jest.fn(data => console.log('HIT FUNCTION with '   data));
    const props = {buttonText: 'Click me', myFunction: mockUpdate};

    render(
        <Provider store={store}>
            <Component {...props}/>
        </Provider>
    );

    userEvent.click(screen.queryByTestId('test'));

    expect(mockUpdate).toHaveBeenCalled();
    expect(mockUpdate).toHaveBeenCalledWith(123);
});

I have also tried moving the myFunction: mockUpdate from the props object into the mockStore({}) initial object, but still no luck...

CodePudding user response:

Your mock myFunction didn't invoke when you click the button. Because the returned value of mapDispatchToProps will become the props of the component.

You can get the mock myFunction via the second parameter of mapDispatchToProps function named ownProps. You may want to call the mock myFunction when dispatching the action. If so, expect(mockUpdate).toHaveBeenCalled(); assertion will work.

component.tsx:

import React from 'react';
import { connect } from 'react-redux';
import { myAction } from './actions';

const mapDispatchToProps = (dispatch, ownProps) => {
  console.log('Your mock myFunction is here:', ownProps.myFunction);
  // But below myFunction is not a mock function and is passed into your component finally.
  return {
    myFunction: (data) => dispatch(myAction(data)),
  };
};

const Component = ({ buttonText, myFunction }) => (
  <button data-testid="test" onClick={() => myFunction(123)}>
    {buttonText}
  </button>
);

export default connect(null, mapDispatchToProps)(Component);
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import createMockStore from 'redux-mock-store';
import userEvent from '@testing-library/user-event';
import Component from './component';

it('Should pass', async () => {
  const mockStore = createMockStore();
  const store = mockStore();

  render(
    <Provider store={store}>
      <Component buttonText="Click me" />
    </Provider>
  );

  await userEvent.click(screen.getByTestId('test'));
  expect(store.getActions()).toEqual([{ type: 'MY_ACTION', agentData: 123 }]);
});

Test result:

 PASS  stackoverflow/75173986/component.test.tsx (8.625 s)
  ✓ Should pass (92 ms)

  console.log
    Your mock myFunction is here: [Function: mockConstructor] {
      _isMockFunction: true,
      getMockImplementation: [Function (anonymous)],
      mock: [Getter/Setter],
      mockClear: [Function (anonymous)],
      mockReset: [Function (anonymous)],
      mockRestore: [Function (anonymous)],
      mockReturnValueOnce: [Function (anonymous)],
      mockResolvedValueOnce: [Function (anonymous)],
      mockRejectedValueOnce: [Function (anonymous)],
      mockReturnValue: [Function (anonymous)],
      mockResolvedValue: [Function (anonymous)],
      mockRejectedValue: [Function (anonymous)],
      mockImplementationOnce: [Function (anonymous)],
      mockImplementation: [Function (anonymous)],
      mockReturnThis: [Function (anonymous)],
      mockName: [Function (anonymous)],
      getMockName: [Function (anonymous)]
    }

      at Function.mapDispatchToProps [as mapToProps] (stackoverflow/75173986/component.tsx:6:11)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |     100 |      100 |     100 |     100 |                   
 actions.ts    |     100 |      100 |     100 |     100 |                   
 component.tsx |     100 |      100 |     100 |     100 |                   
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.103 s, estimated 10 s
Ran all test suites related to changed files.

CodePudding user response:

I found a solution by combining something similar to @Lin Du and also some other resources I found online (that I can't seem to find in my search history to link to at the moment).

This solution doesn't require any additional args such as testProps to be added to the mapDispatchToProps function.

import configureStore from 'redux-mock-store';

...

it('Should call next step with both call plan and caller display', () => {
    const store = { /* any mapStateToProps data here */ };
    const mockStore = configureStore()(store);
    mockStore.dispatch = jest.fn(); // Can add callback in here to update the non-mock store

    const props = {buttonText: 'Click me'};

    provider = getComponentProvider(ChooseCallPlan, store, {wizard}, true);
    render(
        <Provider store={mockStore}>
           <Component {...props}/>
        </Provider>
    );

    userEvent.click(screen.queryByTestId('test'));

    export(mockStore.dispatch).toHaveBeenCalled();
    expect(mockStore.dispatch)
        .toHaveBeenCalledWith(myAction(123));
});
  • Related