Topics Covered
- Make use of Headless Chrome
- Execute test cases in parallel
- Style Cleanup and Reseting Testing Module
- Mock Data Correctly
- Best Practice
Make use of Headless Chrome
Headless Chrome is a way to run the Chrome browser in a headless environment without the full browser UI. One of the benefits of using Headless Chrome (as opposed to testing directly in Node) is that your JavaScript tests will be executed in the same environment as users of your site. Headless Chrome gives you a real browser context without the memory overhead of running a full version of Chrome.
Configuration in karma.conf.js
browsers: ['Chrome', 'ChromeHeadlessNoSandbox']
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--disable-gpu',
'--remote-debugging-port=9222',
'--disable-site-isolation-trials',
]
}
}
More reading on this configuration from developers.google.com
Configurations in package.json file:
{
"test-fast": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng test --browsers=ChromeHeadlessNoSandbox --watch=true --codeCoverage=true --source-map=false"
}
npm run test-fast
Execute test cases in parallel
We can utilize karma-parallel plugin to execute test cases parallely. This npm package splits your unit tests into multiple suites that run in parallel with each other, on different threads of your processor. It's highly customizable straight from your karma config file with how many threads you want to use, and how it splits your tests up.
Configuration details:
- Install package as dev dependency
npm i karma-parallel --save-dev
- Changes in karma.conf.js
frameworks: ['parallel', 'jasmine', ...others] // <- 'parallel' should be first one here
plugin: [
... others,
require: ('karma-parallel')
],
parallelOptions: {
executors: require('os') ? Math.ceil(require('os').cpus().length / 2) : 1
shardStrategy: 'round-robin'
}
NOTE:
- Though this plugin is awesome, the only problem that I have faced with this is inconsistency in calculation code coverage.
- I used this plugin until Angular v11. Starting v12, Angular has made many performance boost to Testing Module and since then, I did not felt use to include this!
Style Cleanup and Reseting Testing Module
If you adjust your karma config to run without a headless browser, and using the inspector, inspect your <head>
tag, you will notice hundreds if not thousands of <style>
tags appended to your body. One more point, after running every test case angular recompiles our test bed configuration, meaning we spent 70% of total time in compilation rather than actual execution of test case. To overcome that we need to override default test bed rest module.
NOTE:
- This section make sense till Angular v11.
- Post v12 Angular has made many performance boost to Testing Module and we can use (in test.ts file):
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule(),
{ teardown: { destroyAfterEach: true } } // <- this is the new entry
);
- This is default in v13 and onwards to no need to make any changes in test.ts, you reap this feature by default (unless you choose to opt-out during upgrade)
Create new file override-reset-test-module-jasmine.ts:
import { getTestBed, TestBed, ComponentFixture } from '@angular/core/testing';
export function overrideResetTestModule() {
const testBedApi: any = getTestBed();
const originReset TestBed.resetTestingModule;
beforeAll(() => {
TestBed.resetTestingModule();
TestBed.resetTestingModule = () => TestBed;
});
afterEach(() => {
testBedApi._activeFixtures.forEach((fixture: ComponentFixture<any>) => fixture.destroy());
testBedApi._instantiated = false;
cleanStylesFromDOM();
});
afterAll(() => {
TestBed.resetTestingModule = originReset;
TestBed.resetTestingModule();
});
function cleanStylesfromDOM(): void {
const head: HTMLHeadElement = document.getElementsByTagName('head')[0];
const styles: HTMLCollectionOf<HTMLStyleElement> I [] = document.getElementsByTagName('style');
for (let i = 0; i < styles.length; i++) {
head.removeChild(styles[i]);
}
}
In you spec file, import this helper file:
describe('', => {
overrideResetTestModule();
// rest of code
});
Mock Data Correctly
- Declare components instead of importing modules
- Always mock data. Especially if your service is interacting with HTTP or Router.
- This is how I prefer to mock http and router
- This is very good and detailed article that explains more on importance of mocking and how to mock data
Best Practice
- A very good article that I refered back in 2017 when I started writing unit test cases.
- Another best article on Resolving common technical debt to speed up Angular development
I follow all these practices on my medium sized project in Angular v13 having around 1500 test cases and execution time is less than a minute (~50 seconds)! Yes less than a minute!!
Top comments (0)