docs: rewrite testing docs · angular/angular@71cde39 · GitHub
Skip to content

Commit 71cde39

Browse files
JeanMechekirjs
authored andcommitted
docs: rewrite testing docs
Those rewrites focus on using Vitest instead of jasmine, drop usages of `fakeAsync`, present modern testing strategy that rely on `whenStable` more than explicit calls to `detectChanges`. fixes #42748, #48510, #64962, #65987, #66029, #66150
1 parent 456ca35 commit 71cde39

18 files changed

Lines changed: 1233 additions & 517 deletions

adev/src/app/routing/sub-navigation-data.ts

Lines changed: 5 additions & 5 deletions

adev/src/content/examples/forms-overview/src/app/app.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {Component} from '@angular/core';
2-
import {FavoriteColorReactiveComponent} from './reactive/favorite-color/favorite-color.component';
3-
import {FavoriteColorTemplateComponent} from './template/favorite-color/favorite-color.component';
2+
import {FavoriteColorReactive} from './reactive/favorite-color/favorite-color.component';
3+
import {FavoriteColorTemplate} from './template/favorite-color/favorite-color.component';
44

55
@Component({
66
selector: 'app-root',
77
templateUrl: './app.component.html',
88
styleUrls: ['./app.component.css'],
9-
imports: [FavoriteColorTemplateComponent, FavoriteColorReactiveComponent],
9+
imports: [FavoriteColorTemplate, FavoriteColorReactive],
1010
})
1111
export class AppComponent {
1212
title = 'forms-intro';

adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
22

33
import {createNewEvent} from '../../shared/utils';
4-
import {FavoriteColorReactiveComponent} from './favorite-color.component';
4+
import {FavoriteColorReactive} from './favorite-color.component';
55

66
describe('Favorite Color Component', () => {
7-
let component: FavoriteColorReactiveComponent;
8-
let fixture: ComponentFixture<FavoriteColorReactiveComponent>;
7+
let component: FavoriteColorReactive;
8+
let fixture: ComponentFixture<FavoriteColorReactive>;
99

10-
beforeEach(waitForAsync(() => {
11-
TestBed.configureTestingModule({
12-
declarations: [FavoriteColorReactiveComponent],
13-
});
14-
}));
15-
16-
beforeEach(() => {
17-
fixture = TestBed.createComponent(FavoriteColorReactiveComponent);
10+
beforeEach(async () => {
11+
fixture = TestBed.createComponent(FavoriteColorReactive);
1812
component = fixture.componentInstance;
19-
fixture.detectChanges();
13+
await fixture.whenStable();
2014
});
2115

2216
it('should create', () => {

adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import {FormControl, ReactiveFormsModule} from '@angular/forms';
66
template: ` Favorite Color: <input type="text" [formControl]="favoriteColorControl" /> `,
77
imports: [ReactiveFormsModule],
88
})
9-
export class FavoriteColorReactiveComponent {
9+
export class FavoriteColorReactive {
1010
favoriteColorControl = new FormControl('');
1111
}
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,44 @@
1-
import {ComponentFixture, fakeAsync, TestBed, tick, waitForAsync} from '@angular/core/testing';
1+
import {ComponentFixture, TestBed} from '@angular/core/testing';
22

33
import {createNewEvent} from '../../shared/utils';
4-
import {FavoriteColorTemplateComponent} from './favorite-color.component';
4+
import {FavoriteColorTemplate} from './favorite-color.component';
55

66
describe('FavoriteColorComponent', () => {
7-
let component: FavoriteColorTemplateComponent;
8-
let fixture: ComponentFixture<FavoriteColorTemplateComponent>;
7+
let component: FavoriteColorTemplate;
8+
let fixture: ComponentFixture<FavoriteColorTemplate>;
99

10-
beforeEach(waitForAsync(() => {
11-
TestBed.configureTestingModule({
12-
declarations: [FavoriteColorTemplateComponent],
13-
});
14-
}));
15-
16-
beforeEach(() => {
17-
fixture = TestBed.createComponent(FavoriteColorTemplateComponent);
10+
beforeEach(async () => {
11+
fixture = TestBed.createComponent(FavoriteColorTemplate);
1812
component = fixture.componentInstance;
19-
fixture.detectChanges();
13+
await fixture.whenStable();
2014
});
2115

2216
it('should create', () => {
2317
expect(component).toBeTruthy();
2418
});
2519

2620
// #docregion model-to-view
27-
it('should update the favorite color on the input field', fakeAsync(() => {
28-
component.favoriteColor = 'Blue';
29-
30-
fixture.detectChanges();
21+
it('should update the favorite color on the input field', async () => {
22+
component.favoriteColor.set('Blue');
3123

32-
tick();
24+
await fixture.whenStable();
3325

3426
const input = fixture.nativeElement.querySelector('input');
35-
3627
expect(input.value).toBe('Blue');
37-
}));
28+
});
3829
// #enddocregion model-to-view
3930

4031
// #docregion view-to-model
41-
it('should update the favorite color in the component', fakeAsync(() => {
32+
it('should update the favorite color in the component', async () => {
4233
const input = fixture.nativeElement.querySelector('input');
4334
const event = createNewEvent('input');
4435

4536
input.value = 'Red';
4637
input.dispatchEvent(event);
4738

48-
fixture.detectChanges();
39+
await fixture.whenStable();
4940

50-
expect(component.favoriteColor).toEqual('Red');
51-
}));
41+
expect(component.favoriteColor()).toEqual('Red');
42+
});
5243
// #enddocregion view-to-model
5344
});
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import {Component} from '@angular/core';
1+
import {Component, signal} from '@angular/core';
22
import {FormsModule} from '@angular/forms';
33

44
@Component({
55
selector: 'app-template-favorite-color',
66
template: ` Favorite Color: <input type="text" [(ngModel)]="favoriteColor" /> `,
77
imports: [FormsModule],
88
})
9-
export class FavoriteColorTemplateComponent {
10-
favoriteColor = '';
9+
export class FavoriteColorTemplate {
10+
favoriteColor = signal('');
1111
}

adev/src/content/guide/forms/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ Here are the steps performed in the model to view test.
304304

305305
1. Use the component instance to set the value of the `favoriteColor` property.
306306
1. Run change detection through the test fixture.
307-
1. Use the `tick()` method to simulate the passage of time within the `fakeAsync()` task.
307+
1. Use `await fixture.whenStable()` to wait for the next rendering.
308308
1. Query the view for the form input element.
309309
1. Assert that the input value matches the value of the `favoriteColor` property in the component instance.
310310

adev/src/content/guide/routing/testing.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ Testing routing and navigation is essential to ensure your application behaves c
66

77
This guide assumes you are familiar with the following tools and libraries:
88

9-
- **[Jasmine](https://jasmine.github.io/)** - JavaScript testing framework that provides the testing syntax (`describe`, `it`, `expect`)
10-
- **[Karma](https://karma-runner.github.io/)** - Test runner that executes tests in browsers
9+
- **[Vitest](https://vitest.dev/)** - JavaScript testing framework that provides the testing syntax (`describe`, `it`, `expect`)
1110
- **[Angular Testing Utilities](guide/testing)** - Angular's built-in testing tools ([`TestBed`](api/core/testing/TestBed), [`ComponentFixture`](api/core/testing/ComponentFixture))
1211
- **[`RouterTestingHarness`](api/router/testing/RouterTestingHarness)** - Test harness for testing routed components with built-in navigation and component testing capabilities
1312

@@ -326,6 +325,6 @@ export class Search {
326325
1. **Use RouterTestingHarness** - For testing routed components, use [`RouterTestingHarness`](api/router/testing/RouterTestingHarness) which provides a cleaner API and eliminates the need for test host components. It offers direct component access, built-in navigation, and better type safety. However, it isn't as suitable for some scenarios, such as testing named outlets, where you may need to create custom host components.
327326
2. **Handle external dependencies thoughtfully** - Prefer real implementations when possible for more realistic tests. If real implementations aren't feasible (e.g., external APIs), use fakes that approximate the real behavior. Use mocks or stubs only as a last resort, as they can make tests brittle and less reliable.
328327
3. **Test navigation state** - Verify both the navigation action and the resulting application state, including URL changes and component rendering.
329-
4. **Handle asynchronous operations** - Router navigation is asynchronous. Use `async/await` or [`fakeAsync`](api/core/testing/fakeAsync) to properly handle timing in your tests.
328+
4. **Handle asynchronous operations** - Router navigation is asynchronous. Use `async/await` to properly handle timing in your tests.
330329
5. **Test error scenarios** - Include tests for invalid routes, failed navigation, and guard rejections to ensure your application handles edge cases gracefully.
331330
6. **Do not mock Angular Router** - Instead, provide real route configurations and use the harness to navigate. This makes your tests more robust and less likely to break on internal Angular updates, while also ensuring you catch real issues when the router updates since mocks can hide breaking changes.

adev/src/content/guide/testing/attribute-directives.md

Lines changed: 119 additions & 15 deletions

0 commit comments

Comments
 (0)