Angular is a nice framework, isn’t it? ? But like every tool, it has its weaknesses. A definite shortcoming is, for example, writing integration tests for templates and component classes. The TestBed testing module provided is simply unpleasant. Can we safely say that such basic things as:
- mocking up the component dependencies
- access to a rich set of assertions (only basic ones from Jasmine)
- catching elements (fixture.debugElement, brrr!!)
- simple test cases, e.g. if “is button disabled”
Are they simple and clear? For me… definitely not! And at this point, it comes in all white:
The spectator is a pretty thoughtful abstraction on the built-in angular testing module. The main author is Netanel Basal, who also runs an angular blog – https://netbasal.com, highly recommended! The library makes test writing, readability, and maintenance significantly easier.
The main advantages of the library:
- support for testing pipes, components, directives, HTTP services and routing
- simplified catching of DOM tree elements (like in jQuery?)
- richer API for running events (Mouse helpers and Keyboard helpers)
- comfortable testing with HostComponent
- support for ng-content testing
- more matchers, such as .toBeDisabled() or .toBeFocused()
- support for entry components and component providers
- service auto-location, wow!
- support for IS
- global queries:
1 2 3 |
const element = spectator.query('.overlay', { root: true, }) |
Let’s check it!
Let’s compare what the test elements in Spectator might look like compared to the TestBed Module, with simple examples.
- Disabled button status:
1 2 3 4 5 6 |
// SPECTATOR expect(spectator.query('.button')).toBeDisabled(); // TEST BED const button = fixture.debugElement.query(By.css('.button')); expect(button.nativeElement.disabled).toBeTruthy(); |
2. Does *ngFor display 2 components in a list:
1 2 3 4 5 6 |
// SPECTATOR expect('app-tshirt-item').toHaveLength(2); // TEST BED const items = fixture.debugElement.queryAll(By.directive(TshirtItemComponent)); expect(items.length).toBe(2); |
3. Locking the service as a component dependency:
1 2 3 4 5 6 7 8 9 10 |
// SPECTATOR providers: [mockProvider(AuthService)], // mockProvider ze Spectator // TEST BED providers: [ { provide: AuthService, useClass: MockAuthService, // nasza zamockowana klasa }, ] |
4. Calling “enter” on input:
1 2 3 4 5 6 7 |
// SPECTATOR const input = spectator.query('input'); spectator.keyboard.pressEnter(input); // TEST BED const input = fixture.debugElement.query(By.css('input')); input.triggerEventHandler('keydown.enter', {}); |
5. Whether the element contains the text:
1 2 3 4 5 6 7 |
// SPECTATOR const title = spectator.query('h1'); expect(title).toContainText('Page title'); // TEST BED const title = fixture.debugElement.query(By.css('h1')); expect(title.nativeElement.textContent).toContain('Page title'); |
There is no denying that it looks much simpler and clearer in Spectator! You will find dozens of examples in the documentation: https://github.com/ngneat/spectator.
Installation
If I’ve convinced you of Spectator, there’s nothing left to do but add it to your project along with ng-mocks via npm:
1 |
npm i -D @netbasal/spectator ng-mocks |
Schematics
It is also worth mentioning that Spectator has its Schematics. For example, to generate a test file for a component, we can use the command:
1 |
ng g cs path/component-name |
Bonus!
If you code using Visual Studio Code, be sure to add the snippet plugin:
Practice with a playground from the authors:
Enjoy testing! ?
Leave a Reply