This repo demonstrates how to use the ng-mocks and jasmine-auto-spies to write better Angular unit tests. A companion article for this repo can be found on Medium.
Clone or fork this repo
git clone https://github.com/bobbyg603/ng-testing-tips-ng-mocks
Install the dependencies
cd ng-testing-tips-ng-mocks && npm i
Run the sample application
npm run start
Run the tests
npm test
Using MockComponent, we can easily declare fake components with all the propert inputs
and outputs
and use them in our tests.
declarations: [
AppComponent,
MockComponent(FormComponent),
MockComponent(CardComponent)
],
Declaring mocked providers is also easy to do with the help of MockProvider.
providers: [
MockProvider(DogService, dogService)
]
Ditto for MockModule.
imports: [
MockModule(FontAwesomeModule),
MockModule(MatProgressSpinnerModule),
MockModule(MatToolbarModule)
],
We can actually forgo all this noise by using MockBuilder.
await MockBuilder(AppComponent, AppModule)
.mock(FontAwesomeModule)
.mock(MatProgressSpinnerModule)
.mock(MatToolbarModule)
.mock(CardComponent)
.mock(DialogComponent)
.mock(FormComponent)
.provide({ provide: DogService, useValue: dogService });
MockRender can be used as a drop in replacement for TestBed, and is super helpful for rendering custom templates and testing directives. For more information on MockRender
see the companion article.
We can use find, findAll, detectChanges, componentInstance, nativeElement, and querySelector to help us run expectations against child component inputs and outputs.
describe('template', () => {
beforeEach(() => {
rendered.detectChanges();
cardComponents = ngMocks.findAll(CardComponent).map(c => c.componentInstance);
formComponent = ngMocks.find<FormComponent>('app-form').componentInstance;
});
it('should render title', () => {
expect(rendered.nativeElement.querySelector('span.title')?.textContent).toMatch(app.title);
});
it('should pass breed to form', () => {
expect(formComponent.breed).toBe(app.breed);
});
it('should pass breeds to form', () => {
expect(formComponent.breeds).toBe(breeds);
});
it('should pass count to form', () => {
expect(formComponent.count).toBe(app.count);
});
it('should create card for each dog', () => {
dogs.forEach(dog => {
expect(cardComponents.find(c => c.imgSrc === dog)).toBeTruthy();
});
});
it('should show spinner when loading', () => {
rendered.componentInstance.loading$ = of(true);
rendered.detectChanges();
expect(ngMocks.find(MatProgressSpinner)).toBeTruthy();
});
it('should not show spinner when not loading', () => {
rendered.componentInstance.loading$ = of(false);
rendered.detectChanges();
expect(ngMocks.findAll(MatProgressSpinner).length).toBe(0);
});
it('should call onFormChange when form component raises formChange event', () => {
const event = { breed: 'affenpinscher', count: 3 };
const spy = spyOn(rendered.componentInstance, 'onFormChange');
formComponent.formChange.emit(event);
expect(spy).toHaveBeenCalledWith(event);
});
});
If you found this repo valuable please subscribe to @bobbyg603 on Medium for more Angular tips and tricks.
Thanks for reading!