With conservative estimation, each developer saves at least 3 hours a week on running angular unit tests. Before I was spending about 50 minutes a day waiting for tests to run. Now, tests run so fast I hardly have time to get distracted by banter in #random.
Don’t Load More Than You Need To
The biggest feedback loop win has been the cryptically named NO_ERRORS_SCHEMA. The official documentation says, “Defines a schema that will allow any property on any element.”. It could be better described as “Render only components that are declared in your module, while courageously ignoring errors related to unknown components.”. Each test ran between 3 to 5 times faster after implementing this schema.
Each component test declares a very minimalist module. The module provides us with only the components which are important for executing the tests.
We learned that in most cases you only need to render a single component for each test.
Each component test defines a test module. The test module can import components by defining import modules. Previously we were defining over 100 components in the test module.
The key is in only declaring the component under test and adding NO_ERRORS_SCHEMA to your test module. This means that we don’t render the whole tree of components. A three to five time speed increase for each test was realised with this change.
x TestBed.configureTestingModule({
declarations: [
AppComponent
],
imports: [],
providers: [],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
// full example:
https://gist.github.com/ebuckley/980baffb8a46a3cfe1b14434123e07ad#file-goodtest-component-spec-ts-L14
On line 14 we include the NO_ERRORS_SCHEMA which allows us to declare only the component under test.
You should consider some of the disadvantages of NO_ERRORS_SCHEMA. When a component renders all child components, a lot of incidental testing happens.
Testing component interactions is something you need to be explicit about when using this schema. Each component that causes or reacts to side effects will need to be declared in the testing module.
Run Only the Relevant Tests
When you’re developing tests for a new component, focus on the tests that are important. In jasmine, we have the “fdescribe” and “fit” function for this purpose.
As you work on tests, natural groups of tests will appear. When this happens, it makes sense to wrap the tests in a “describe” function. Focusing on this logical group of tests is now possible!
Replace PhantomJS With Chrome Headless
When a test fails on CI while passing locally developers around the world perform a collective facepalm. In our project, phantomJS is frequently the culprit. As of April 2017 chrome headless is the solution.
Using chrome, our tests run about 30% faster in the CI environment. This was just the icing on the cake though. Our true win has been in having a consistent environment for local and CI test running. We have had no DOM behaviour inconsistencies between test environments.
If you’re using Angular CLI with Jasmine/Karma for tests, try it out.
- We need karma-chrome-launcher first. “npm install –save-dev karma-chrome-launcher”
- Copy the customLaunchers section from line 52
customLaunchers: {
chrome_headless: {
base: 'Chrome',
flags: [
'--headless',
'--no-sandbox',
'--disable-gpu',
' --remote-debugging-port=9222'
]
}
}
// see a full example: https://gist.github.com/ebuckley/980baffb8a46a3cfe1b14434123e07ad#file-karma-conf-js-L52
3. Update your test command to use the new launcher
ng lint && ng test --single-run --no-sourcemap --browsers 'chrome_headless'
// see the full example: https://gist.github.com/ebuckley/980baffb8a46a3cfe1b14434123e07ad#file-package-json-L13
Wrapping Things Up
If I tried to list all the ways you can speed up tests, you would never have time to go and make these easy wins. We refactored over 100 tests and the developer happiness really shows.
If your stack looks like ours, then you can probably implement these speed ups too. Go out, be courageous and take action. Make your tests fast, your team will not regret it.
Banner image: Photo by insung yoon on Unsplash