Jest async action creators:TypeError: Cannot read property 'then' of undefined

1.1k Views Asked by At

Kinda newbie to jest here.I am trying to write unit test cases for one of the async action creators in my React project using jest. I keep running into the error TypeError: Cannot read property 'then' of undefined

Below is my action creator:

import {loginService} from "./services";

export function login(email: string, password: string): (dispatch: ThunkDispatch<{}, {}, any>) => void {
  return dispatch => {
    dispatch(loggingIn(true));
    loginService(email, password).then(
      (response: any) => {
        dispatch(loggingIn(false));
        dispatch(loginAction(response));
      },
      error => {
       //Code
        }
        dispatch(loggingIn(false));
        dispatch(loginError(true, message));
      }
    );
  };
}

./services.js

export const loginService = (username: string, password: string) => {
  const requestOptions = {
    method: "POST",
    headers: {
      //Headers
    },
    body: JSON.stringify({email: username, password: password})
  };
  return fetch(`url`, requestOptions)
    .then(handleResponse, handleError)
    .then((user: any) => {
      //code
      return user;
    });
};

Given below is my test:

it("login", () => {
    fetchMock
      .postOnce("/users/auth", {
        body: JSON.parse('{"email": "user", "password": "password"}'),
        headers: {"content-type": "application/json"}
      })
      .catch(() => {});
    const loginPayload = {email: "user", password: "password"};
    const expectedSuccessActions = [
      {type: types.LOGGING_IN, payload: true},
      {type: types.LOGIN, loginPayload}
    ];
    const expectedFailureActions = [
      {type: types.LOGGING_IN, payload: true},
      {type: types.LOGIN_ERROR, payload: {val: true, errorMessage: "error"}}
    ];
    const store = mockStore({user: {}});
    const loginService = jest.fn();
    return store.dispatch(LoginActions.login("email", "password")).then(() => {
      expect(store.getActions()).toEqual(expectedSuccessActions);
    });
  });

Please help

1

There are 1 best solutions below

2
On

The end result returned by dispatching your LoginActions.login() action is void (or undefined). Not a Promise, so not something you can call .then() on in your test.

Judging by your test code, you're using fetch-mock for your fetchMock. You should be able to wait for that to finish before testing that the store dispatched the correct actions:

it("login", async () => {
//          ^^^^^ --> note that you need to make your test async to use await

    store.dispatch(LoginActions.login("email", "password"));
    await fetchMock.flush();

    expect(store.getActions()).toEqual(expectedSuccessActions);
});

Note that the comments in your code seem to indicate that your loginService does some more things before returning from the .then() callback. If that takes too long, waiting for the fetchMock to finish might not be waiting long enough. In that case, you should consider returning the Promise from your LoginActions.login() action so that you can test it. Whether or not you should depends on how much effort it would be to adjust your application to handle that, since you don't want your app to crash with any unhandled promise rejection errors in case the login fails.