Angular : Initialize your tests in a beforeAll

Valentin PARSY
Frontend Weekly
Published in
3 min readJan 23, 2018
Warning : this is an hazardous practice

When testing an angular component, you initialize your tests via the TestBed class, which helps you mock services, modules etc … You usually do it in a beforeEach function at the top of your describe.

Not so long ago, someone came by my desk and told me something along those lines : “Your component always depends on the same services/modules, you should put this initialization in a beforeAll, it’ll save a ton of time !”
And I though “OMG that sounds so logical, why didn’t I think of that sooner ?!”

So I did just that and … it didn’t work.

The Problem

When you do it, you realise pretty fast your component has lost its links to services and actually everything else that is exterior to it. In other words, it didn’t initialyze at all.
This matter has been adressed already on the Angular GIT :

You can see in a comment by awerlang that putting the TestBed’s init in a beforeAll doesn’t work because Angular resets the modules before each test :
Ideally, we should be able to call TestBed.configureTestingModule() inside a beforeAll(). It just doesn't work because of this line(FYI a line in the definition of beforeEach in angular/core). This is reseting the modules before each test. Perhaps it shouldn't matter, but it does because Angular spends quite a lot of time compiling components in JiT mode. Reseting it throws away this information. Recording a profiling session on karma window led me to this conclusion.”

The (not so safe) solution

There is a solution, a very unstable solution, use it with caution. Angular resets the modules with a call to a function (TestBed.resetTestingModule), so what you can do is override this function before your tests, and put back this function at the end of your tests (so that Angular works correctly afterward).
This is what this code looks like :

import { TestBed, TestModuleMetadata } from '@angular/core/testing';const resetTestingModule = TestBed.resetTestingModule;
const preventAngularFromResetting = () => TestBed.resetTestingModule = () => TestBed;
const allowAngularToReset = () => {
resetTestingModule();
TestBed.resetTestingModule = resetTestingModule;
};
export const setUpTestBed = (moduleDef: TestModuleMetadata, ...funcs: (() => void)[]) => { beforeAll(done => (async () => {
resetTestingModule();
preventAngularFromResetting();

TestBed.configureTestingModule(moduleDef);
funcs.forEach(func => func());

TestBed.resetTestingModule = () => TestBed;
return await TestBed.compileComponents();
})().then(done).catch(done.fail));

afterAll(() => allowAngularToReset());
};

And you use it like that :

describe('My test that might work', () => {  setUpTestBed({
imports: [
MyModule
],
declarations: [
MyComponent
],
schemas: [
NO_ERRORS_SCHEMA,
],
});
it('tests something', ()=>{})});

Using this solution will throw errors most of the time, but when it doesn’t you can gain some time on your tests.
Is the unstability of this solution worth the time you gain ?

--

--

Valentin PARSY
Frontend Weekly

Javascript enthusiast. Developper at @sfeir. Follow me at @ParsyValentin