Les nouveautés d'Angular 19 en 4 minutes

Angular 19 vient de sortir, et il y a beaucoup de nouveautés intéressantes : hydratation incrémentale, linkedSignal, l'API des ressources, et plein d'autres choses. Venez découvrir tout ça en moins de 4 minutes !

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Tester un Observable utilisé dans un composant

Voici le code:

ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UsersComponent } from './users.component';
import { UserService } from 'src/app/core/services/user.service';
import { of } from 'rxjs';

describe('UsersComponent', () => {
    let component: UsersComponent;
    let fixture: ComponentFixture<UsersComponent>;
    let userServiceStub: Partial<UserService>;

    beforeEach(async () => {
        userServiceStub = {
            users$: of([{ id: 1, name: 'John', email: '[email protected]' }, { id: 2, name: 'Jane', email: '[email protected]' }]),
            getAll: jasmine.createSpy('getAll').and.returnValue(of())
        };

        await TestBed.configureTestingModule({
            declarations: [UsersComponent],
            providers: [{ provide: UserService, useValue: userServiceStub }]
        })
        .compileComponents();

        fixture = TestBed.createComponent(UsersComponent);
        component = fixture.componentInstance;
    });

    it('should fetch users on initialization', () => {
        component.ngOnInit();
        expect(userServiceStub.getAll).toHaveBeenCalled();
    });

    it('should have users populated from observable', () => {
        component.users$.subscribe(users => {
            expect(users.length).toBe(2);
            expect(users[0].name).toBe('John');
            expect(users[1].name).toBe('Jane');
        });
    });
});
ts
import { Component, OnInit } from '@angular/core'
import { Observable } from 'rxjs';
import { UserService } from 'src/app/core/services/user.service';
import { User } from 'src/app/core/user';

@Component({
    selector: 'app-users',
    templateUrl: 'users.component.html'
})
export class UsersComponent implements OnInit {
    users$: Observable<User[]> = this.userService.users$

    constructor(private userService: UserService) { }

    ngOnInit() {
        this.userService.getAll().subscribe()
    }
}
ts
import { HttpClient } from "@angular/common/http";
import { Injectable, inject } from "@angular/core";
import { BehaviorSubject, Observable, tap } from "rxjs";
import { User } from "../user.interface";

@Injectable({
  providedIn: "root",
})
export class UserService {
  private http = inject(HttpClient);
  private _users$: BehaviorSubject<User[]> = new BehaviorSubject([] as User[]);
  readonly url: string = "https://jsonplaceholder.typicode.com/users";
  readonly users$: Observable<User[]> = this._users$.asObservable();

  getAll(): Observable<User[]> {
    return this.http.get<User[]>(this.url).pipe(
      tap((users: User[]) => {
        this._users$.next(users);
      })
    );
  }
}
ts
export interface User {
    id: number;
    name: string;
    username?: string;
    email: string;
    address?: {
        street: string;
        suite: string;
        city: string;
        zipcode: string;
        geo: {
            lat: string;
            lng: string;
        }
    };
    phone?: string;
    website?: string;
    company?: {
        name: string;
        catchPhrase: string;
        bs: string;
    };
}

Explication du test

Tester le retour de l'observable est essentiel pour s'assurer que le composant fonctionne comme prévu avec les données qu'il reçoit. Pour ce faire, nous allons ajuster le test précédent pour s'assurer que users$ est correctement alimenté par le service.

1. Le Stub de UserService:

typescript
userServiceStub = {
    users$: of([{ id: 1, name: 'John', email: '[email protected]' }, { id: 2, name: 'Jane', email: '[email protected]' }]),
    getAll: jasmine.createSpy('getAll').and.returnValue(of())
};

Lorsque nous testons des composants dans Angular, nous souhaitons souvent les isoler de leurs dépendances externes pour ne tester que le composant lui-même. C'est ce qu'on appelle un test unitaire. UserService est une dépendance externe pour UsersComponent.

Le code ci-dessus crée un faux service, ou "stub", pour UserService. Plutôt que d'utiliser le vrai service, qui pourrait faire des appels HTTP ou avoir d'autres comportements que nous ne souhaitons pas inclure dans notre test, nous utilisons ce stub.

  • users$: C'est un Observable qui renvoie une liste d'utilisateurs. Dans un vrai scénario, cela pourrait être une liste récupérée d'une API. Mais pour notre test, nous utilisons of() de rxjs pour créer un Observable qui renvoie simplement une liste prédéfinie d'utilisateurs.

  • getAll: Dans le vrai UserService, cette méthode pourrait faire un appel HTTP pour récupérer une liste d'utilisateurs. Ici, nous utilisons jasmine.createSpy(). Cela crée une fonction "espion" qui nous permet de suivre si elle a été appelée et avec quels arguments. and.returnValue(of()) indique que cette fonction espion doit simplement renvoyer un Observable vide lorsqu'elle est appelée.

2. Fournir le Stub à TestBed:

typescript
providers: [{ provide: UserService, useValue: userServiceStub }]

TestBed est la principale API d'Angular pour tester des composants et d'autres éléments. Il nous permet de créer un environnement de test pour notre composant.

  • provide: UserService: Cela indique à Angular que nous allons fournir quelque chose pour remplacer le UserService normal dans ce contexte de test.

  • useValue: userServiceStub: C'est ici que nous disons à Angular d'utiliser notre faux service (stub) à la place du vrai UserService. Ainsi, lorsque notre composant demande une instance de UserService, il recevra notre stub à la place.

3. Tester l'Observable:

typescript
component.users$.subscribe(users => {
    expect(users.length).toBe(2);
    expect(users[0].name).toBe('John');
    expect(users[1].name).toBe('Jane');
});

Le code ci-dessus teste le comportement de l'observable users$ dans notre composant.

  • component.users$.subscribe(): Cela souscrit à l'observable users$. Lorsque cet observable émet des valeurs (c'est-à-dire envoie des données), la fonction à l'intérieur de subscribe() est exécutée.

Chaque mois, recevez en avant-première notre newsletter avec les dernières actualités, tutoriels, astuces et ressources Angular directement par email !