Skip to content

Commit 9542b88

Browse files
post: angular unit testing with spectator (#65)
* post: angular unit testing with spectator
1 parent 69715b7 commit 9542b88

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

_data/authors.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,8 @@ Edgar Barragan:
8484
name : "Edgar Garcia Barragan"
8585
avatar : "assets/images/avatars/edgar_barragan.png"
8686
bio : "Senior Android Engineer - Customer Success"
87+
88+
Utku:
89+
name : "Utku"
90+
avatar : "assets/images/avatars/utku.jpeg"
91+
bio : "Frontend Engineer - Customer Success"
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
---
2+
title: "Angular Unit Testing with Spectator"
3+
excerpt: "A robust testing library for unit testing"
4+
tags: Web Angular testing
5+
authors:
6+
- Utku
7+
header:
8+
teaser: /assets/images/post/spectator-banner.png
9+
teaser_alt: Spectator banner
10+
category: Frontend
11+
---
12+
13+
![](/assets/images/post/spectator-banner.png)
14+
15+
# Introduction
16+
17+
Spectator is a powerful testing tool designed to simplify Angular testing. It is built on top of the TestBed API and effectively reduces the boilerplate code associated with the built-in TestBed API. By doing so, Spectator delivers a more efficient and developer-friendly testing experience. In this article, we discuss Spectator test cases, installation, testing components and services, advantages, drawbacks, and migrating to Spectator.
18+
19+
# Understanding Spectator Test Cases
20+
21+
Spectator supports three main types of test cases:
22+
23+
1. **Isolated unit tests**:
24+
Corresponding to [**Component class testing**](https://angular.io/guide/testing-components-basics#component-class-testing) mentioned in Angular.io.
25+
These tests focus on testing component, directive, pipe, and service classes in isolation. Additionally, Spectator supports writing tests for routing and HTTP communication. It is essential to mock dependencies to ensure tests are isolated.
26+
27+
2. **Shallow component tests**:
28+
Corresponding to [**Component DOM testing**](https://angular.io/guide/testing-components-basics#component-dom-testing) mentioned in Angular.io.
29+
Shallow component tests focus on testing a component with a template, but ignoring the rendering of child components. By passing the 'shallow: true' option into the configuration, we can achieve this quite easily with Spectator.
30+
31+
3. **Integration tests**:
32+
Corresponding to [**Component DOM testing**](https://angular.io/guide/testing-components-basics#component-dom-testing) mentioned in Angular.io.
33+
Integration tests focus on testing how two or more components work together. These tests are particularly useful when components depend on each other.
34+
35+
In the following sections, we delve deeper into the installation process, testing components and services, and the advantages and drawbacks of using Spectator.
36+
37+
## Installing Spectator
38+
39+
To install Spectator, you can use either yarn or npm:
40+
41+
`yarn add @ngneat/spectator --dev`
42+
43+
`npm install @ngneat/spectator --save-dev`
44+
45+
Spectator uses **Jasmine** by default, but it supports **Jest** as well.
46+
To be able to use it with Jest, make sure to target your imports from `@ngneat/spectator/jest` instead of `@ngneat/spectator`.
47+
48+
# Testing Components with Spectator
49+
50+
When testing components with Spectator, the first step is to create a component factory using the `createComponentFactory` function and pass the component class you want to test. This factory function returns a new component for each test block. Spectator offers various options for the `createComponentFactory` function, allowing you to customize your testing environment.
51+
52+
```typescript
53+
import { Spectator, createComponentFactory } from "@ngneat/spectator";
54+
import { ButtonComponent } from "./button.component";
55+
56+
describe("ButtonComponent", () => {
57+
let spectator: Spectator<ButtonComponent>;
58+
const createComponent = createComponentFactory(ButtonComponent);
59+
60+
beforeEach(() => (spectator = createComponent()));
61+
62+
it("should have a success class by default", () => {
63+
expect(spectator.query("button")).toHaveClass("success");
64+
});
65+
66+
it("should set the class name according to the [className] input", () => {
67+
spectator.setInput("className", "danger");
68+
expect(spectator.query("button")).toHaveClass("danger");
69+
expect(spectator.query("button")).not.toHaveClass("success");
70+
});
71+
});
72+
```
73+
74+
The `createComponentFactory` function can optionally take the following options which extends the basic Angular Testing Module options:
75+
76+
```typescript
77+
const createComponent = createComponentFactory({
78+
component: ButtonComponent,
79+
imports: [],
80+
providers: [],
81+
declarations: [],
82+
entryComponents: [],
83+
componentProviders: [], // Override the component's providers
84+
componentViewProviders: [], // Override the component's view providers
85+
overrideModules: [], // Override modules
86+
mocks: [], // Providers that will automatically be mocked
87+
componentMocks: [], // Component providers that will automatically be mocked
88+
componentViewProvidersMocks: [], // Component view providers that will be automatically mocked
89+
detectChanges: false, // Defaults to true
90+
declareComponent: false, // Defaults to true
91+
disableAnimations: false, // Defaults to true
92+
shallow: true, // Defaults to false
93+
});
94+
```
95+
96+
After creating the component factory, you can use the `createComponent` function to test different aspects of your component, such as setting inputs, testing outputs, or running detectChanges().
97+
98+
The `createComponent` function optionally takes the following options:
99+
100+
```typescript
101+
it("should...", () => {
102+
spectator = createComponent({
103+
// The component inputs
104+
props: {
105+
title: "Click",
106+
},
107+
// Override the component's providers
108+
providers: [],
109+
// Whether to run change detection (defaults to true)
110+
detectChanges: false,
111+
});
112+
113+
expect(spectator.query("button")).toHaveText("Click");
114+
});
115+
```
116+
117+
The `createComponent()` method returns an instance of `Spectator` which exposes the following properties:
118+
119+
- `fixture` - The tested component's fixture
120+
- `component` - The tested component's instance
121+
- `element` - The tested component's native element
122+
- `debugElement` - The tested fixture's debug element
123+
124+
And the following methods:
125+
126+
- **inject()**
127+
<br>
128+
Provides a wrapper for Ivy's `TestBed.inject()`:
129+
130+
```typescript
131+
const service = spectator.inject(QueryService);
132+
const fromComponentInjector = true;
133+
const service = spectator.inject(QueryService, fromComponentInjector);
134+
```
135+
136+
- **detectChanges()**
137+
<br>
138+
Runs `detectChanges` on the tested element/host:
139+
140+
```typescript
141+
spectator.detectChanges();
142+
```
143+
144+
- **setInput()**
145+
<br>
146+
Changes the value of an `@Input()` of the tested component:
147+
148+
```typescript
149+
it("should...", () => {
150+
spectator.setInput("className", "danger");
151+
152+
spectator.setInput({
153+
className: "danger",
154+
});
155+
});
156+
```
157+
158+
- **output()**
159+
<br>
160+
Returns an observable `@Output()` of the tested component:
161+
162+
```typescript
163+
it("should emit the $event on click", () => {
164+
let output;
165+
spectator.output("click").subscribe((result) => (output = result));
166+
167+
spectator.component.onClick({ type: "click" });
168+
expect(output).toEqual({ type: "click" });
169+
});
170+
```
171+
172+
- **tick(millis?: number)**
173+
<br>
174+
Run the fakeAsync `tick` function and call `detectChanges()`:
175+
```typescript
176+
it("should work with tick", fakeAsync(() => {
177+
spectator = createComponent(ZippyComponent);
178+
spectator.component.update();
179+
expect(spectator.component.updatedAsync).toBeFalsy();
180+
spectator.tick(6000);
181+
expect(spectator.component.updatedAsync).not.toBeFalsy();
182+
}));
183+
```
184+
185+
# Testing Services with Spectator
186+
187+
To test services with Spectator, import the `createServiceFactory` and `SpectatorService` functions, and then create a service factory using the `createServiceFactory` function.
188+
189+
```typescript
190+
import { createServiceFactory, SpectatorService } from "@ngneat/spectator";
191+
192+
import { AuthService } from "auth.service.ts";
193+
194+
describe("AuthService", () => {
195+
let spectator: SpectatorService<AuthService>;
196+
const createService = createServiceFactory(AuthService);
197+
198+
beforeEach(() => (spectator = createService()));
199+
200+
it("should not be logged in", () => {
201+
expect(spectator.service.isLoggedIn()).toBeFalsy();
202+
});
203+
});
204+
```
205+
206+
The `createService` function returns `SpectatorService` with the following properties:
207+
208+
- `service` - Get an instance of the service
209+
- `inject()` - A proxy for Angular `TestBed.inject()`
210+
211+
**For detailed examples, further information and other use cases, please refer to the official Spectator documentation:** [https://github.com/ngneat/spectator](https://github.com/ngneat/spectator/)
212+
213+
# Generating Spec Files with Angular CLI and Spectator Schematics
214+
215+
Creating spec files with Spectator configuration is made simple with the provided schematics.
216+
217+
To generate spec files:
218+
219+
For a component spec, run: `ng g cs example-component`
220+
221+
For a service spec, run: `ng g ss example-service`
222+
223+
For a directive spec, run: `ng g ds example-directive`
224+
225+
# Advantages of Using Spectator
226+
227+
Testing can sometimes be more challenging and time-consuming than the actual implementation. Configuring TestBed correctly may even take longer than writing the tests themselves. Spectator significantly improves the developer experience by making it easier and faster to write tests. As a result, it helps overcome the tendency to skip writing tests. Spectator achieves this by:
228+
229+
- Simplifying test spec configuration by providing readable and concise API which helps to reduce boilerplate code.
230+
- Improving readability and maintainability by providing a set of expressive and easier to understand custom matchers and assertions.
231+
- Making mocking and spying easier.
232+
233+
# Drawbacks of Using Spectator
234+
235+
- Learning curve might be considered steep.
236+
- Official documentation might be considered relatively limited.
237+
- Compatibility issues may arise with newer Angular version even though it might be temporary.
238+
239+
# Migrating
240+
241+
Spectator can be adopted gradually since it is not a complete replacement rather an enhancement. Whenever Spectator test suits are started to be written, these test suits can be kept in a separate file with `spectator` suffix. For example, `transactions.component.spectator.spec.ts`. Once the project is fully migrated to spectator, the `spectator` suffix can be omitted from all test file names.
242+
243+
For integration tests, there is also an approach that to create a separate test file with `integration` suffix to keep integration tests separate from rest of the test suites. For example, `transactions.component.integration.spec.ts`.
244+
245+
With a proper migration plan in place, developers can smoothly transition from their existing testing setup to Spectator without disrupting the project.
246+
247+
# Conclusion
248+
249+
By leveraging the power of Spectator, developers can enjoy a more efficient and user-friendly testing experience. With its simplified test spec configuration, improved readability, and enhanced mocking capabilities, Spectator is a valuable addition to any Angular project. Although it comes with a learning curve, the advantages it offers make it a worthwhile investment for improving your testing workflow. For developers looking to streamline their Angular testing process, Spectator is a powerful and versatile tool that can help you achieve your goals.
250+
251+
# References and Additional Resources
252+
253+
- [Spectator Documentation](https://github.com/ngneat/spectator)
254+
- [testing-angular.com](https://testing-angular.com/testing-components-with-spectator/#testing-components-with-spectator)
255+
- An in depth comparison between regular test specs and spectator test specs can be found in this open-source project: [https://github.com/9elements/angular-workshop](https://github.com/9elements/angular-workshop). <br> Each test spec in this project has a jasmine and a spectator alternative allowing to make comparison as well as serving as a reference for additional use cases.

assets/images/avatars/utku.jpeg

22 KB
Loading
48.7 KB
Loading

0 commit comments

Comments
 (0)