Skip to content

Why?

Jest has been flipping defaults with version 27. It is explained in the Jest blog from May 25, 2021:

»Up until now, if you were using Jest in its default configuration, you were - perhaps unknowingly - running some code forked many years ago from the test runner Jasmine 2.0 ...
... In the next major, we plan to also eliminate "jest-jasmine2" and "jest-environment-jsdom" from the Jest dependency tree and require them to be installed explicitly, so that many users can profit from a smaller install size with less clutter that they don't need.«
Jest blog

How can you identify if you are using jest-jasmine2?

If you are running »some code forked many years ago« you can find out by

  • running npx jest --showConfig
  • looking in your jest.config.js files

Either way will tell you about the testRunner you are using.

testRunner: 'jest-jasmine2',

How to proceed?

Step one: replace everything

At leanix we are using a nx monorepo. This enables us to remove the jest-jasmine2 test runners per nx library. Removing it from one jest.config.js file will only affect existing tests that rely on this configuration.

The easiest way to remove the jasmine references from your test is by replacing commonly used jasmine patterns.

Search and replacements examples

The following expression are examples. You might need to adapt them for your own project.

SearchReplace
spyOn()spyOn((.+?, '\w+?'));jest.spyOn($1);
.callThrough()spyOn((.+?, '\w+?')).and.callThrough();jest.spyOn($1);
spyOnspyOn(jest.spyOn(
jasmine.createSpy();jasmine.createSpy();jest.fn()
.calls.mostRecent();.calls.mostRecent();.mock.calls[0];
jasmine failfail,(error) => {throw new Error(error);},
jasmine.arrayContaining()jasmine.arrayContaining(expect.arrayContaining(
jasmine.objectContaining()jasmine.objectContaining(expect.objectContaining(
.throwError()spyOn((.+?, '\w+?')).and.throwError((.+?));jest.spyOn($1).mockImplementation(() => {throw new Error($2)});
.callFake()spyOn((.+?, '\w+?')).and.callFake((.+?));jest.spyOn($1).mockImplementation($2);
jasmine.Spy;: jasmine.Spy;: jest.SpyInstance;
.returnValue()spyOn((.+?, '\w+?')).and.returnValue(jest.spyOn($1).mockReturnValue(
.returnValues()spyOn((\w+?, '\w+?')).and.returnValues((.+?));jest.spyOn($1).mockReturnValue($2);

returnValues() needs manual adjustments (or a niftier regex). I did not adapt this since it is not used very often in our codebase and manual replacement did not cause much effort.

This is an example how the replacement could look like in the end:

$2 => .mockReturnValue(a).mockReturnValue(b)

Step two: fix occurring issues

This tends to be the more time-consuming step. If you have replaced all the above jasmine references, just run your test for this library/app that you have been working on and fix the tests that are now failing.

Frequently occurring issues:

  • There are still more jasmine references left (It depends for example on your code formatting and if your regular expressions span multiple lines. Also, not all possible references are mentioned above.) Just keep on replacing.
  • The jest.spyOn() function by default calls through. That means if some of the underlying logic uses state and requires some values to be defined it will fail. An easy fix is to add .mockReturnValue() to the spy that causes the issue.
  • A test fails when run in line with other tests, but does not fail, when run alone. This could mean that a spy is not restored during testing. It could be fixed by calling jest.restoreAllMocks() in an afterEach() block.
afterEach(() => jest.restoreAllMocks());
  • "TypeError: Converting circular structure to JSON" might hint to an issue with async testing. Try setting up async and await properly.
  • "Test functions cannot both take a 'done' callback and return something." and "Expected done to be called once, but it was called multiple times." could mean that you could remove your done callback function entirely for this test.
  • ngMocks MockInstance uses jasmine. You would have to replace the MockComponent() in your test declarations with the actual component and provide everything that is needed to fix the tests. Depending on how often MockInstance is used in your code this could turn out to be quite some work.

Outlook

Everything is working again, and you want to provide some help for your team to write tests without jasmine. Here is a cheat sheet for you:

Cheat sheet to write future tests without using jasmine:

If your tests are suddenly failing, try restoring your mocks after each test:

afterEach(() => {
jest.restoreAllMocks();
});

If you just want to restore one mock between tests use:

afterEach(() => {
spy.mockRestore();
})

jasmine.callThrough();

JASMINE

spyOn(someService, "someFunction").and.callThrough();

JEST

jest.spyOn(someService, "someFunction");

(callThrough behaviour is default in jest)


.createSpy();

JASMINE

jasmine.createSpy()

JEST

jest.fn()

.calls.mostRecent();

JASMINE

.calls.mostRecent();

JEST

.mock.calls[0];

jasmine.Spy;

JASMINE

jasmine.Spy;

JEST

jest.SpyInstance;

.and.returnValue();

JASMINE

spyOn(someService, "someFunction").and.returnValue("a")

JEST

jest.spyOn(someService, "someFunction").mockReturnValue("a")

.and.returnValues();

JASMINE

spyOn(someService, "someFunction").and.returnValues(["a", "b"]);

JEST

jest.spyOn(someService, "someFunction").mockReturnValueOnce("a").mockReturnValueOnce("b");

.mockReturnValueOnce for each value you want to return

spyOn();

JASMINE

spyOn(someService, "someFunction");

JEST

jest.spyOn(someService, "someFunction");

.throwError();

JASMINE

spyOn(someService, "someFunction").and.throwError("Error");

JEST

jest.spyOn(someService, "someFunction").mockImplementation(()=> {throw new Error("Error")});

fail

JASMINE

testObservable$.subscribe(()=> {}, fail, done)

JEST

testObservable$.subscribe(() => {}, (error)=> {throw new Error(error)}, done)

Or replace the subscribe entirely with something like await firstValueFrom(x) instead.


jasmine.arrayContaining() & jasmine.objectContaining()

JASMINE

jasmine.arrayContaining();
jasmine.objectContaining();

JEST

expect.arrayContaining();
expect.objectContaining();

.callFake()

JASMINE

spyOn(someService, "someFunction").and.callFake(()=> {return "a"});

JEST

spyOn(someService, "someFunction").mockImplementation(()=> {return "a"});

jasmine.anything()

JASMINE

jasmine.anything()

JEST

expect.anything()
Image of the author

Published by Dahlia de Candido

Visit author page