What is the best way to mock in Jest Unit Test this package?

Viewed 783

Good day! I'm working with Amplitude is a service for monitoring your actions etc.. but returning to my question, at this moment i'm trying to mock this module "amplitude-js" to do Test, but i think i'm mocking in a bad way.. so if someone can help me to do this in the right way i will appreciate. So starting first with the function that i want to do test is this one:

export const signIn = (): ThunkAction<
  void,
  RootState,
  null,
  FluxStandardAction
> => {
  return async (dispatch, getState) => {
    const { username, password } = getState().Authentication;
    amplitude.getInstance().logEvent('LOGIN_SIGN_IN_REQUEST'); <=== This
    dispatch(signInRequest() as FluxStandardAction<string, any>);

    try {
      const response = await axios.post(`${API}/users/login`, {
        username,
        password,
      });
      const { data } = response;
      amplitude.getInstance().logEvent('LOGIN_SIGN_IN_SUCCESS', data); <=== This as well
      dispatch(signInSuccess(data.token) as FluxStandardAction<string, any>);
    } catch (err) {
      Sentry.captureException(err);
      dispatch(signInFailure(err) as FluxStandardAction<string, any>);
    }
  };
};

As you see im using redux to dispatch the action, i did the Test of my function without trying to mock Amplitude and the test is Perfect.. i will share my code of my test that you can see what i have.. remember i only want to test Amplitude, the others values i dont care!!

THE TEST:


jest.mock('axios');
jest.mock('amplitude-js');

import axios from 'axios';
import amplitude from 'amplitude-js';

import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { API } from '../../../src/Config';
import * as actions from '../../../src/redux/actions/authentication/authentication';

const middleware = [thunk];
const mockStore = configureStore(middleware);
const store = mockStore({});
const mockT = jest.fn().mockReturnValue('LOGIN_SIGN_IN_REQUEST');

describe('Authentication async action', () => {
  describe("'onLogin' actions", () => {
    beforeEach(() => {
      store.clearActions();
    });
    it('creates LOGIN_SIGN_IN_SUCCESS on successful authentication', async () => {
      const token = 'token';

      (axios.post as jest.Mock).mockReset();
      (axios.post as jest.Mock).mockResolvedValue({
        data: {
          token,
        },
      });

      (amplitude.getInstance as any).mockReset();

      const expectedActions = [
        {
          type: actions.LOGIN_SIGN_IN_REQUEST,
        },
        {
          type: actions.LOGIN_SIGN_IN_SUCCESS,
          payload: token,
        },
      ];
      const store = mockStore({
        Authentication: {
          username: 'username',
          password: 'password',
        },
      });

      await store.dispatch(actions.signIn() as any);
      expect(amplitude.getInstance().logEvent('LOGIN_SIGN_IN_REQUEST'));
      expect(store.getActions()).toEqual(expectedActions);
      expect(axios.post).toHaveBeenCalledWith(`${API}/users/login`, {
        username: 'username',
        password: 'password',
      });
    });

So when i run the test in Jest i'm receiving this error:


  ● Authentication async action › 'onLogin' actions › creates LOGIN_SIGN_IN_SUCCESS on successful authentication

    TypeError: Cannot read property 'logEvent' of undefined

      103 |   return async (dispatch, getState) => {
      104 |     const { username, password } = getState().Authentication;
    > 105 |     amplitude.getInstance().logEvent('LOGIN_SIGN_IN_REQUEST');
          |     ^
      106 |     dispatch(signInRequest() as FluxStandardAction<string, any>);
      107 | 
      108 |     try {

      at src/redux/actions/authentication/authentication.ts:105:5

If i call this:

describe('Authentication async action', () => {
  describe("'onLogin' actions", () => {
    beforeEach(() => {
      store.clearActions();
    });
    it('creates LOGIN_SIGN_IN_SUCCESS on successful authentication', async () => {
      const token = 'token';

      (axios.post as jest.Mock).mockReset();
      (axios.post as jest.Mock).mockResolvedValue({
        data: {
          token,
        },
      });

      (amplitude.getInstance() as any).mockReset(); <=== i call getsIstance()

      const expectedActions = [
        {
          type: actions.LOGIN_SIGN_IN_REQUEST,
        },
        {
          type: actions.LOGIN_SIGN_IN_SUCCESS,
          payload: token,
        },
      ];
      const store = mockStore({
        Authentication: {
          username: 'username',
          password: 'password',
        },
      });

      await store.dispatch(actions.signIn() as any);
      expect(amplitude.getInstance().logEvent('LOGIN_SIGN_IN_REQUEST'));
      expect(store.getActions()).toEqual(expectedActions);
      expect(axios.post).toHaveBeenCalledWith(`${API}/users/login`, {
        username: 'username',
        password: 'password',
      });
    });

Now i received this error:


    TypeError: Cannot read property 'mockReset' of undefined

      36 |       });
      37 | 
    > 38 |       (amplitude.getInstance() as any).mockReset();
         |       ^
      39 | 
      40 |       const expectedActions = [
      41 |         {

      at Object.<anonymous> (test/redux/Authentication/Authentication.action.async.test.ts:38:7)

Quite annoying..

Maybe is something easy to mock.. but i don't have a lot of experience doing test!

1 Answers

i'm going with

beforeEach(() => {
  window.amplitude = {
    getInstance: jest.fn(() => ({ logEvent: jest.fn() })),
  };
});

we'll see if it passes code review. you might also need setUserProperties etc. in there depending on which components you're rendering/testing and what they do

may also need something in afterEach to clean it up, not sure. would be great if anyone out there can chime in

Related