📢 Je vous présente le livre Angular

  • 1) Il offre un contenu clair et concis, tout en couvrant une multitude de concepts d'Angular.
  • 2) Le livre est structurĂ© en trois niveaux : dĂ©butant, intermĂ©diaire et avancĂ©
  • 3) traite des pratiques les plus rĂ©centes d'Angular, comme les signaux, les vues diffĂ©rĂ©es, la gestion des flux, entre autres
  • 4) De plus, vous y trouverez plusieurs liens vers des exemples de code source pour approfondir vos connaissances en pratique.

Skip to content

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: 'john@test.com' }, { id: 2, name: 'Jane', email: 'jane@test.com' }]),
            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: 'john@test.com' }, { id: 2, name: 'Jane', email: 'jane@test.com' }]),
    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 !