Fixing Broken Tests
Basics
ng test
.- If you want to generate a code coverage report, run
ng test --no-watch --code-coverage
.
- If you want to generate a code coverage report, run
- You can also limit tests to a directory:
ng test --include='src/app/path/**/*.spec.ts'
.- It also supports patterns:
ng test --include='src/app/[n-z]*/**/*.spec.ts'
ng test --include='src/app/admin/[d,g-i]*/**/*.spec.ts'
- It also supports patterns:
Disabling Tests
If you would like to skip a test, you can either change describe()
to xdescribe()
or it()
to xit()
.
Skipped tests will display in the Karma report in a yellow-green color, and as a pending spec.
Fixing Common Errors
Error: Found the synthetic property @slideInAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.
Import NoopAnimationsModule
, unless you need the other:
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ],
imports: [ NoopAnimationsModule ]
}).compileComponents();
}));
Error: NG0302: The pipe 'stringArray' could not be found!
Error: NG0302: The pipe 'yesNoIndicator' could not be found!
Error: NG0302: The pipe 'yesNoUnknownIndicator' could not be found!
Required pipes need to be added to declarations
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProviderViewComponent, StringArrayPipe, YesNoIndicatorPipe, YesNoUnknownIndicatorPipe ]
})
.compileComponents();
});
NullInjectorError: No provider for ActivatedRoute!
Import RouterTestingModule
:
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ AdminFacilityComponent ],
imports: [ RouterTestingModule ]
})
.compileComponents();
}));
NullInjectorError: No provider for FormBuilder!
Import ReactiveFormsModule
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminCredentialUpsertComponent ],
imports: [ ReactiveFormsModule ]
})
.compileComponents();
});
NullInjectorError: No provider for HttpClient!
Import HttpClientTestingModule
:
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ FileUploadComponent ],
imports: [ HttpClientTestingModule ]
})
.compileComponents();
}));
NullInjectorError: No provider for MatDialog!
Import MatDialogModule
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminCredentialTableComponent ],
imports: [ MatDialogModule ]
})
.compileComponents();
});
NullInjectorError: No provider for MatDialogRef!
NullInjectorError: No provider for InjectionToken MatDialogData!
Update providers
to provide MatDialogRef
and MAT_DIALOG_DATA
:
describe('AdminProviderEmploymentsInsertDialogComponent', () => {
let component: AdminProviderEmploymentsInsertDialogComponent;
let fixture: ComponentFixture<AdminProviderEmploymentsInsertDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminProviderEmploymentsInsertDialogComponent ],
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: { } }
]
})
.compileComponents();
});
});
You may pass an object via useValue
if needed, such as { provide: MAT_DIALOG_DATA, useValue: { credentialId: 0 } }
.
NullInjectorError: No provider for MatSnackBar!
Import MatSnackBarModule
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminCredentialComponent ],
imports: [ MatSnackBarModule ]
})
.compileComponents();
});
NullInjectorError: No provider for Router!
Import RouterTestingModule
:
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AdminImportPreprocessGuard],
imports: [ RouterTestingModule ]
});
});
TypeError: Cannot read properties of null (reading 'paramMap')
The component likely has some code like the following:
this.facilityId = +this.route.snapshot.parent.paramMap.get('id');
(The order of these may vary; you'll want to make note of the order and make sure that the following fix has the same order.)
You'll want to configure the route in the providers
of your test, like the following example:
beforeEach(async () => {
await TestBed.configureTestingModule({
providers: [
{ provide: ActivatedRoute, useValue: { snapshot: { parent: { paramMap: convertToParamMap({ id: -1 }) } } } }
]
})
.compileComponents();
});
This isn't specific to paramMap
. For example:
//this.vendor = this.route.snapshot.data.vendor;
{ provide: ActivatedRoute, useValue: { snapshot: { data: { vendor: TEST_VENDOR } } } }
Error: No value accessor for form control with name: '...'
If this references a ng-select
if your template HTML, import NgSelectModule
(and likely NoopAnimationsModule
if you haven't already) and add the declaration NgSelectFormFieldControlDirective
:
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupSpecialtyUpsertComponent, NgSelectFormFieldControlDirective ],
imports: [ NgSelectModule, NoopAnimationsModule ]
})
.compileComponents();
If using mat-checkbox
, import MatCheckboxModule
, etcetera.
Fixing Common Console-Only Errors
These errors typically don't show in the browser, but will show in the console that ran ng test
.
ERROR: 'NG0303: Can't bind to 'routerLink' since it isn't a known property of 'a'.'
ERROR: 'NG0304: 'router-outlet' is not a known element:
Import RouterTestingModule
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesComponent ],
imports: [ RouterTestingModule ]
})
.compileComponents();
});
ERROR: 'NG0303: Can't bind to 'ngModel' since it isn't a known property of 'input'.'
Import FormsModule
:
await TestBed.configureTestingModule({
declarations: [ CommonListCrudTableComponent ],
imports: [ FormsModule ]
})
.compileComponents();
ERROR: 'NG0303: Can't bind to 'formGroup' since it isn't a known property of 'form'.'
ERROR: 'NG0303: Can't bind to 'formControl' since it isn't a known property of 'form'.'
Import ReactiveFormsModule
:
await TestBed.configureTestingModule({
declarations: [ SpecialtyUpsertComponent ],
imports: [ ReactiveFormsModule ]
})
.compileComponents();
ERROR: 'NG0304: 'mat-card' is not a known element:
ERROR: 'NG0304: 'mat-card-title' is not a known element:
ERROR: 'NG0304: 'mat-card-content' is not a known element:
ERROR: 'NG0304: 'mat-card-actions' is not a known element:
Import MatCardModule
:
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupUpsertComponent ],
imports: [ MatCardModule, MatSnackBarModule, ReactiveFormsModule ],
providers: [
{ provide: SpecialtyClient, useValue: mockSpecialtyClient }
]
})
.compileComponents();
ERROR: 'NG0304: 'mat-form-field' is not a known element:
ERROR: 'NG0304: 'mat-label' is not a known element:
ERROR: 'NG0304: 'mat-error' is not a known element:
Import MatFormFieldModule
, MatInputModule
, and NoopAnimationsModule
:
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupUpsertComponent ],
imports: [ MatFormFieldModule, MatInputModule, NoopAnimationsModule, ReactiveFormsModule ],
providers: [
{ provide: SpecialtyClient, useValue: mockSpecialtyClient }
]
})
.compileComponents();
ERROR: 'NG0303: Can't bind to 'dataSource' since it isn't a known property of 'table'.'
ERROR: 'NG0303: Can't bind to 'matHeaderRowDef' since it isn't a known property of 'tr'.'
ERROR: 'NG0303: Can't bind to 'matRowDefColumns' since it isn't a known property of 'tr'.'
Import MatTableModule
:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupTableComponent ],
imports: [ MatCardModule, MatDialogModule, MatTableModule ]
})
.compileComponents();
});
ERROR: 'NG0304: 'mat-datepicker' is not a known element:
Import MatDatepickerModule
and MatMomentDateModule
(unless using a different DateAdapter
):
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupSpecialtyUpsertComponent ],
imports: [ MatDatepickerModule, MatMomentDateModule ]
})
.compileComponents();
ERROR: 'NG0304: 'mat-icon' is not a known element:
Import MatIconModule
:
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesGroupComponent ],
imports: [ MatIconModule, MatSnackBarModule ],
providers: [
{ provide: SpecialtyClient, useValue: mockSpecialtyClient }
]
})
.compileComponents();
ERROR: 'NG0304: 'mat-nav-list' is not a known element:
MatListModule
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminSpecialtiesComponent ],
imports: [ MatListModule, RouterTestingModule ]
})
.compileComponents();
});
ERROR: 'NG0304: 'mat-paginator' is not a known element:
Import MatPaginatorModule
:
TestBed.configureTestingModule({
declarations: [ ProviderSearchComponent ],
imports: [ MatPaginatorModule, MatTableModule ],
providers: [
{ provide: ProviderClient, useValue: mockProviderClient }
]
})
.compileComponents();
ERROR: 'NG0304: 'app-...' is not a known element:
Where 'app-...'
is the name of a custom component, create and declare a fake component, including any @Input()
s:
describe('DemographicsGenderComponent', () => {
let component: DemographicsGenderComponent;
let fixture: ComponentFixture<DemographicsGenderComponent>;
@Component({
selector: 'app-demographics-gender-table',
template: ''
})
class FakeDemographicsGenderTableComponent {
@Input() genders;
}
beforeEach(async () => {
// ...
await TestBed.configureTestingModule({
declarations: [ DemographicsGenderComponent, FakeDemographicsGenderTableComponent ],
imports: [ MatIconModule, MatSnackBarModule ],
providers: [{ provide: DemographicsClient, useValue: mockDemographicsClient }]
})
.compileComponents();
});
// ...
});
TypeError: Cannot read properties of undefined (reading 'something')
Where something
is an @Input()
, set it when creating the component before running tests:
describe('AddressViewComponent', () => {
let component: AddressViewComponent;
let fixture: ComponentFixture<AddressViewComponent>;
// Addition 1.
const TEST_ADDRESS: IAddressViewModel = {
addressType: new AddressTypeViewModel()
};
beforeEach(async () => {
// ...
});
beforeEach(() => {
fixture = TestBed.createComponent(AddressViewComponent);
component = fixture.componentInstance;
// Addition 2.
component.address = TEST_ADDRESS;
fixture.detectChanges();
});
// ...
});
TinyMCE
For components that use TinyMCE, it's best to fake it for testing. If you do not, you'll end up with random errors mentioning length
and open
.
@Component({
selector: 'editor',
template: '',
providers: [
{ provide: NG_VALUE_ACCESSOR, useValue: { writeValue: () => {}, registerOnChange: () => {}, registerOnTouched: () => {} }, multi: true }
]
})
class FakeEditorComponent {
@Input() init;
}
// ...
await TestBed.configureTestingModule({
declarations: [ FakeEditorComponent ]
// ...
})
.compileComponents();
// ...
.NET API Swagger Clients
NullInjectorError: No provider for AdminClient!
NullInjectorError: No provider for AuthClient!
NullInjectorError: No provider for CommonClient!
Start by creating a simple mock client that has no methods that should be spied upon.
- Define a variable for the mock client.
- Set it to
jasmine.createSpy()
in thebeforeEach()
. - Add the new mock client in
providers
.
describe('AppAuthService', () => {
// Addition 1.
let mockAuthClient;
beforeEach(() => {
mockAuthClient = jasmine.createSpy();
TestBed.configureTestingModule({
providers: [ { provide: AuthClient, useValue: mockAuthClient } ]
})
});
// ...
});
Once it starts throwing an error, such as TypeError: this.adminClient.adminProviderCredentialsView is not a function
then you can modify your test.
- Swap in
jasmine.createSpyObj()
, passing in any methods that will need to be spied upon. - Try using
.and.returnValue(of(true))
initially, and then tweak it based upon what the component is doing.
describe('AdminFacilityComponent', () => {
let component: AdminFacilityComponent;
let fixture: ComponentFixture<AdminFacilityComponent>;
// Previous addition 1.
let mockFacilityClient;
beforeEach(waitForAsync(() => {
// Change to addition 2.
mockFacilityClient = jasmine.createSpyObj(['getFacilityAdminView']);
mockFacilityClient.getFacilityAdminView.and.returnValue(of(true));
TestBed.configureTestingModule({
declarations: [ AdminFacilityComponent ],
providers: [
// Previous addition 3.
{ provide: FacilityClient, useValue: mockFacilityClient }
]
})
.compileComponents();
}));
// ...
});