- Declare Components in the testing module
- Use the NO_ERRORS_SCHEMA
- Import only what is necessary!
- Module imports should generally be avoided
- Provide Mock Services
- Declare Mock Pipes
- Declare Mock Directives
Don't import the whole module a component is declared in.
Test
import { AppComponent } from 'app/app.component';
...
TestBed.configureTestingModule({
...
declarations: [
AppComponent
],
...
})
Test
import { NO_ERRORS_SCHEMA } from '@angular/core';
...
TestBed.configureTestingModule({
...
schemas: [
NO_ERRORS_SCHEMA
],
...
})
Component
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private store: Store<AppState>,
) {}
}
Test
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
RouterTestingModule // Not Needed! Not used in component
]
declarations: [
AppComponent
]
})
Component
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private authenticationService: AuthenticationService,
) {
this.authenticationService.init()
}
}
Test BAD
TestBed.configureTestingModule({
imports: [
AuthenticationModule // Don't import everything from a module when you only use one part of it, e.g. the service
]
declarations: [
AppComponent
]
})
Test BETTER
TestBed.configureTestingModule({
declarations: [
AppComponent
],
providers: [
AuthenticationService
]
})
Component
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private authenticationService: AuthenticationService,
) {
this.authenticationService.init()
}
}
Test
const authenticationServiceMock: AuthenticationService = {
init(): void {}
};
TestBed.configureTestingModule({
declarations: [
AppComponent
],
providers: [
{
provide: AuthenticationService,
useValue: authenticationServiceMock
}
]
})
Component
@Component({
selector: 'user-birthday',
template: `
<div>{{ user.birthday | date }}</div>
`,
styles: ['']
})
export class UserBirthdayComponent {
@Input()
user: User;
}
Test
Declare a pipe inside the test that has the same name as the pipe used in the component template and add it to the declarations
in the testing module.
@Pipe({
name: 'date'
})
export class MockDatePipe implements PipeTransform {
transform(value: any, ...args: any[]) {
return value;
}
}
...
TestBed.configureTestingModule({
declarations: [
UserBirthdayComponent,
MockDatePipe
]
})
Component
@Component({
selector: 'user-popover',
template: `
<div #popover="ngbPopover">{{ user.name }}</div>
`,
styles: ['']
})
export class UserPopoverComponent implements OnDestroy {
@ViewChild('popover')
popover: NgbPopover;
ngOnDestroy() {
this.popover.close();
}
}
Test
Declare a directive inside the test that has the same selector
and exportAs
as the directive used in the template and add it to the declarations
in the testing module.
Replace all functions from the directive that are called inside your component with empty functions.
@Directive({selector: '[ngbPopover]', exportAs: 'ngbPopover'})
export class NgbPopoverMockDirective {
close(): void {}
}
...
TestBed.configureTestingModule({
declarations: [
UserPopoverComponent,
NgbPopoverMockDirective
]
})