Hello, World!
One of my recent tickets on Angular project I work was to update unit test framework jest to 27.x version. If there was a simple, painless process this article wouldn’t existed. During update process I encountered all possible errors (I think). Searching for a solution on the internet didn’t helped much. Yeah, I updated jest, but it was pretty big study and much time spent for searching. So I decided to make this article so that people who try to update jest to 27.x version didn’t encounter this much errors.
TLDR;
The main feature of this update is setting. If you want to check those errors and how to fix them, go to the third part of this article.
This is the plan of the article:
Updating packages
The most important part of the update IMHO is updating all the necessary packages, related to jest. As I mentioned earlier, the project uses Angular and there was recent update to 12.x version. Right after the update we got ticket to update jest.
Alongside jest in Angular we also need:
- jest – latest version (27.0.6);
- jest-preset-angular – latest version (9.0.5);
- @types/jest – also latest version (27.0.0).
All the necessary packages installed – let’s move on.
Updating settings
Jest team in their blog release big article about jest update, IMHO it lack some specifics. Yes, I have to mention that I handled the update thanks to that article, but I had to re-read it several times.
Crucial moment about the update. If you updating from jest earlier than 15.x, you had to do it yourself. This article only covers update from 15-26 to 27 version.
The first thing to do – update transform.js
Transformers were updated and now this file should look like this:
module.exports = require(“babel-jest”).default.createTransformer({
presets: [require.resolve(“@babel/preset-env”)]
});
Here just berore createTransformer call we have added default.
The next step – updating jest.config.js
Here we have to update the next options due to changed defaults – testEnvironment, moduleDirectories, transform. Changes in jest.config.js:
transform: {
“^.+\\.(ts|html)$”: “jest-preset-angular”
},
testEnvironment: “jsdom”,
moduleDirectories: [
"node_modules",
"src"
],
After this changes most of the errors should disappear, so in the next section I go through the other in-code errors.
Errors and fixes
The first error I encountered TypeError: require(…). createTransformer is not a function.
It appeared to be the simplest error to fix – updating transform.js fixes it.
The second error – Component is not resolved
This error, like the previous one, I get during npm run test. Here’s the error’s code:
Component ‘ComponentName’ is not resolved:
- templateUrl: ./component-name.component.html
- styleUrls: [“./component-name.component.scss”]
Did you run and wait for ‘resolveComponentResources()’?
Looks like we have to resolve components somehow. Strange. One of the solutions found in google was to add TestBed.compileComponents() call, but it was false assumption leading to nowhere.
Updating jest.config.js fixes this problem.
The third error – component is undefined
This one was the most strange error. Google said that I have to fix it the way I have to fix previous one, but after updating jest.config.js the error gone. Here’s the error’s code:
TypeError: Cannot read property ‘someMethod’ of undefined
test(‘should do something’ () => {
component.someMethod(‘foo’);
^
expect(result).toBeTruthy();
I had approximately half of the errors from 600+ test suits with this kind of errors when component was undefined. As I mentioned this error gone after updating jest.config.js.
The next error fixes assume that you have already updated transform.js and jest.config.js
Expected done to be called once, but it was called multiple times
Kind of simple error. Also jest documentation states:
The same
donetest callback may not be called more than once
I had this error reproduced mostly in subscriptions, but there were few places where done was called multiple times. The solutions is simple – move done calls later in code or remove it at all(for a regular code).
Expected done to be called once, but it was called multiple times.
someService.methodWithObservable(‘bar’)
.subscribe((result) => {
expect(result).toEqual([]);
done();
^
});
For Observables we have to slightly change their structure by adding pipe and first (or last) operators, in case if we need to get the data from the ‘last part’ of the subscription:
someService.methodWithObservable(‘bar’)
.pipe().first().subscribe((result) => {
expect(result).toEqual([]);
done();
^
});
In case of calling pipe and first(last) operators, our Observable would emit onlu one portion of the result – first in case of first and last in case of last.
Test functions cannot both take a ‘done’ callback and return something
Same as previous error – easy to do after reading jest documentation. In general the fix is just remove done() or remove return. I had cases only with returns, but you might have both. The error message says all we need to know:
Test functions cannot both take a ‘done’ callback and return something. Either use a ‘done’ callback, or return a promise.
Matcher error: received value must be a mock or spy function
In this case the error is not much of a helper. Let’s see the output from terminal:
Expect(received)
.toHaveBeenCalledTimes(expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function setTimeout]
jest.useFakeTimers();
component.doSomething();
expect(setTimeout)
.toHaveBeenCalledTimes(1);
^
jest.runAllTimers();
Here I spotted jest.useFakeTimers() line, which helped a lot. And it dawned on me that I read the jest update article not in vain:
modern fake timers implementation will now be the default. If you are among the unlucky few who are affected by the subtle implementation differences too heavily to migrate, you can get back the old implementation using jest’s method
useFakeTimers("legacy")or, if you are enabling fake timers globally via configuration,"timers": "legacy".
So I added “legacy” to a useFakeTimers call and it helped. In your case you might want to rewrite the code, but it shouldn’t be much of a problem.
Conclusion
As we see if we dig down into documentation and googling we might ease the process of updating jest to 27.x version and it won’t take much time. If you have any questions feel free to reach me out or comment this article. And please write about your experience of updating jest.
If this article was interesting and you want to know more about me, visit my cv-site.
