The Missing Ingredient for Angular Template Code Coverage and Future-Proof Testing
😧 What's wrong with JIT?
Code coverage is not accurate as the template is not taken into account. It is slower as the tests compile the templates on the fly. It is not future-proof as Angular has reached the limits of JIT compatibility. By design, some features are simply impossible to implement with JIT. It is not symmetrical with the production environment , where AOT is used.
⏰ Why now?
Vitest is the only option that supports AOT compilation. There are open PRs for Karma and Jest Experimental Builder to support AOT.
🎁 Other Benefits of AOT Testing
⚡️ Faster Test Execution
👯 Production-Symmetry
🔮 Future-Proof Tests
🤔 Drawbacks
🪄 Dynamic Component Constructs Should Be Avoided
// 🛑 This is broken with AOT.
const fixture = render(`<app-button/>`, { imports: [Button] });
function render(template, { imports }) {
@Component({
template,
imports,
})
class TestContainer {}
return TestBed.createComponent(TestContainer);
}
function render(template, { imports }) {
@Component({
jit: true,
template,
imports,
})
class TestContainer {}
return TestBed.createComponent(TestContainer);
}
🦦 Shallow Testing is More Challenging
TestBed#overrideComponent
// app.cmp.spec.ts
vi.mock('./street-map.cmp', async () => {
return {
StreetMap: await import('./street-map-fake.cmp').then(
(m) => m.StreetMapFake
),
};
});
// street-map-fake.cmp.ts
@Component({
selector: 'app-street-map',
template: 'Fake Street Map',
})
class StreetMapFake implements StreetMap {
// ...
}
It is less readable and more verbose. "Mocking" (or providing test doubles) at the module level is less granular and can be less predictable. It is highly coupled to the testing framework you are using.
TestBed#overrideComponent
*.jit.spec.ts
👨🏻🍳 Taking Vitest with AOT for a Spin
1. Set up Vitest
For Angular CLI users, use Analog's schematic . For Nx users, choose the vitest
option when generating an application or library (available since Nx 20.1.0).
2. Enable AOT
vite.config.js
jit
false
export default defineConfig({
...
plugins: [
angular({ jit: false }),
...
],
...
});
📈 Configure Code Coverage
istanbul
v8
istanbul
1. Install @vitest/coverage-istanbul
@vitest/coverage-istanbul
Make sure you install the Vitest Istanbul version that matches Vitest's major version
npm install -D @vitest/coverage-istanbul
2. Choose istanbul
as your coverage provider
istanbul
vite.config.mts
export default defineConfig({
...
test: {
...
coverage: {
provider: 'istanbul',
},
},
});
🎬 Watch it in Action
nx test my-app --coverage --ui --watch
# or
ng test --coverage --ui --watch
coverage
It will even work for structural directives.
The coverage also works for inline templates ! 🚀
☢️ Mind the Code Coverage Trap
Any observed statistical regularity will tend to collapse once pressure is placed upon it for control purposes.
-- Charles Goodhart
🚀 What's Next?
Recommended : Migrate to Vitest. (📻 stay tuned as I'll soon be sharing the smoothest migration path) Use the experimental builder with AOT. Wait for jest-preset-angular
AOT support.