Keep Your Code Testable
Separation Between Component and Services
Components can contain a lot of observables and business logic.
To keep components lightweight, we prefer to move all logic into a component service.
The component service exposes observables that the component can expose 1-to-1 to the template.
All interactions in the template are also passed through the component to the service.
⚠️ Component services should not be provided in root, since they are only used in one place.
Component services must not be reused outside the relevant component.
Component services should be provided in the component itself.
When writing unit tests, make sure to properly mock them:
Example Component Using a Component Service
export class component implements OnInit, OnDestroy {
#titleService = inject(TitleService);
#overviewService = inject(OverviewService);
#methodService = inject(MethodService);
#adminService = inject(AsAdminService);
loadingData$ = this.#overviewService.loadingData$;
overviewData$ = this.#overviewService.overviewData$;
asAdmin$ = this.#adminService.asAdmin$;
methodFetchError$ = this.#methodService.methodFetchError$;
ngOnInit() {
this.#titleService.setKey('overview');
}
toggleAsAdmin = () => this.#asAdminService.toggleAsAdmin();
constructor() {
this.#overviewService.initializeDataFetching();
}
ngOnDestroy() {
this.#overviewService.destroy();
}
}
Templates Should Not Communicate with Themselves
If a template communicates directly with itself, it's no longer possible to intercept with a unit test — in this case, you'll need an integration or end-to-end test.
❌ Bad Example
<div>
<button (click)="myComponent.hide()" />
<myComponent #myComponent />
</div>