1

I am trying to mock an external module (jwt_decode for those interested), and I have seen many examples of how to mock external an node module using Jest both for all tests in a test suite, as well as on a per-test basis.

I have been able to mock the dependency so that it mocks the return value for all tests in the suite, although the default function is all that i'm really concerned with.

import jwt_decode from 'jwt-decode';

jest.mock('jwt-decode', () => jest.fn().mockReturnValue({
  exp: 12345,
  somethingElse: 'test_value'
}));

This works well, except I would like to test a scenario where the returned token has expired, so that I can verify that certain Redux actions were dispatched when this situation arises.

import jwt_decode from 'jwt-decode';

const decoded = jwt_decode(localStorage.jwtToken);

// set user info and isAuthenticated
store.dispatch(setCurrentUser(decoded));

// this is the scenario I am looking to test
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
  store.dispatch(logoutUser());
  store.dispatch(clearCurrentProfile());

  window.location.href = '/login';
}

I would like to modify the returned mock for an individual test so that I can ensure that the 'if' statement shown equates to false, and other parts of the code base are executed.

How can one go about this?

Some of the examples I have tried and failed with so far include:

test('some test that will use a different mock' () => {
  // re-assign value of the imported module using the its given alias
  jwt_decode = jest.fn().mockImplementation(() => {
    return {
      exp: 'something_different 9999999999',
      somethingElse: 'I_changed_too'
    };
  });
});

As well as

jwt_decode.default = jest.fn().mockImplementation(() => {
   return {
     exp: 'something_different 9999999999',
     somethingElse: 'I_changed_too'
   };
});

And also jest.spyOn(), as seen in this SO question, as well as A Jar of Clay's answers on the same question, which proposes the following:

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});

I also found a suggestion for creating a util which changes the global localStorage on a test by test basis, but I would rather not use a real jsonwebtoken, or have to worry about storing sign-in credentials.

What I keep ending up with is that jwt_decode is not updated when running the test that should have the different mock value returned, or what's more common is that I get an error saying that ".default is not a function".

If you have suggestions, I would be very grateful.

Oisín Foley
  • 2,317
  • 2
  • 22
  • 29

1 Answers1

1

Assuming I understand your ultimate goal, how about this approach:

In your project directory, at the same level as node_modules, create a directory called "__mocks__" and in that directory, put a file called "jwt-decode.js" -- with this in place there is no need to explicitly mock jwt-decode in your test module, as it will always be mocked automatically.

Put the following code in your __mocks__/jst_decode.js file:

export default token => token;

Thus, when the code that you're testing calls jwt_decode(something), the return value will be exactly what was passed in.

Now you can write unit tests that test the behavior of your module given various valid or invalid values in the token; just mock the token value in your module and your mocked implementation of jwt_decode() will simply pass it through.

Kevin
  • 11
  • 1