Fixing Broken Tests

Basics

  • ng test.
    • If you want to generate a code coverage report, run ng test --no-watch --code-coverage.
  • 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'

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.

  1. Define a variable for the mock client.
  2. Set it to jasmine.createSpy() in the beforeEach().
  3. 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.

  1. Swap in jasmine.createSpyObj(), passing in any methods that will need to be spied upon.
  2. 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();
  }));
  // ...
});