Le développement piloté par les tests est une pratique de programmation prêchée et promue par toutes les communautés de développeurs de la planète. Et pourtant, c'est une routine qui est en grande partie négligée par un développeur tout en apprenant un nouveau cadre. Écrire des tests unitaires dès le premier jour vous aidera à rédiger un meilleur code, à repérer facilement les bogues et à maintenir un meilleur flux de travail de développement..
Angular, en tant que plate-forme de développement frontale à part entière, dispose de ses propres outils de test. Nous allons utiliser les outils suivants dans ce tutoriel:
it ('devrait avoir un composant défini', () => expect (composant) .toBeDefined (););
TestBed
et ComposantFixtures
et des fonctions d'assistance telles que async
et fakeAsync
font partie de la @ angulaire / coeur / test
paquet. Se familiariser avec ces utilitaires est nécessaire si vous souhaitez rédiger des tests qui indiquent comment vos composants interagissent avec leurs propres modèles, services et autres composants..Nous n'allons pas couvrir les tests fonctionnels utilisant Protractor dans ce tutoriel. Protractor est un framework de test populaire de bout en bout qui interagit avec l'interface utilisateur de l'application à l'aide d'un navigateur réel..
Dans ce tutoriel, nous nous intéressons plus aux tests des composants et à la logique du composant. Cependant, nous écrirons quelques tests démontrant l'interaction de base de l'interface utilisateur à l'aide du framework Jasmine..
L'objectif de ce didacticiel est de créer l'interface frontale pour une application Pastebin dans un environnement de développement piloté par des tests. Dans ce tutoriel, nous suivrons le célèbre mantra TDD, qui est "rouge / vert / refactor". Nous écrirons des tests qui échoueront initialement (en rouge), puis nous travaillerons sur notre code d'application pour les faire passer (en vert). Nous allons refactoriser notre code quand il commence à puer, ce qui signifie qu'il est gonflé et laide.
Nous allons écrire des tests pour les composants, leurs modèles, leurs services et la classe Pastebin. L'image ci-dessous illustre la structure de notre application Pastebin. Les éléments grisés seront abordés dans la deuxième partie du didacticiel..
Dans la première partie de la série, nous nous concentrerons uniquement sur la configuration de l'environnement de test et la rédaction de tests de base pour les composants. Angular est un framework à base de composants. par conséquent, il est judicieux de vous familiariser avec l'écriture de tests pour les composants. Dans la deuxième partie de la série, nous allons écrire des tests plus complexes pour les composants, les composants avec entrées, les composants routés et les services. À la fin de la série, nous aurons une application Pastebin entièrement fonctionnelle ressemblant à ceci:.
Vue du composant PastebinVue du composant AddPasteDans ce tutoriel, vous allez apprendre à:
Le code complet du tutoriel est disponible sur Github.
https://github.com/blizzerand/pastebin-angular
Clonez le dépôt et n'hésitez pas à consulter le code si vous avez des doutes à n'importe quel stade de ce tutoriel. Commençons!
Les développeurs d’Angular nous ont facilité la configuration de notre environnement de test. Pour commencer, nous devons d’abord installer Angular. Je préfère utiliser le CLI angulaire. C'est une solution tout-en-un qui prend en charge la création, la génération, la construction et le test de votre projet Angular..
ng new Pastebin
Voici la structure de répertoire créée par Angular-CLI.
Puisque nos intérêts sont davantage orientés vers les aspects de test dans Angular, nous devons rechercher deux types de fichiers..
karma.conf.js est le fichier de configuration du programme d’exécution de test Karma et le seul fichier de configuration dont nous aurons besoin pour écrire des tests unitaires dans Angular. Par défaut, Chrome est le lanceur de navigateur par défaut utilisé par Karma pour capturer des tests. Nous allons créer un lanceur personnalisé pour exécuter le Chrome sans tête et l’ajouter à la navigateurs
tableau.
/*karma.conf.js*/ navigateurs: ['Chrome', 'ChromeNoSandboxHeadless'], customLaunchers: ChromeNoSandboxHeadless: base: 'Chrome', drapeaux: ['--no-sandbox', // voir https: / /chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md '--headless', '--disable-gpu', // Sans port de débogage distant, Google Chrome se ferme immédiatement. '--remote-debugging-port = 9222',],,,
L’autre type de fichier que nous devons rechercher est tout ce qui se termine par .spec.ts
. Par convention, les tests écrits en Jasmine sont appelés spécifications. Toutes les spécifications de test doivent être situées à l'intérieur de l'application. src / app /
répertoire car c’est là que Karma cherche les spécifications de test. Si vous créez un nouveau composant ou un service, il est important de placer vos spécifications de test dans le même répertoire que le code du composant ou du service..
le ng nouveau
commande a créé un app.component.spec.ts
déposer pour notre app.component.ts
. N'hésitez pas à l'ouvrir et jeter un coup d'œil sur les tests Jasmine dans Angular. Même si le code n’a aucun sens, c’est bien. Nous allons conserver AppComponent tel qu'il est pour le moment et l'utiliser pour héberger les itinéraires ultérieurement dans le didacticiel..
Nous avons besoin d'une classe Pastebin pour modéliser notre Pastebin à l'intérieur des composants et des tests. Vous pouvez en créer un en utilisant l'interface angulaire-CLI.
générer la classe Pastebin
Ajoutez la logique suivante à Pastebin.ts:
classe d'exportation Pastebin id: number; titre: chaîne; langue: chaîne; coller: chaîne; constructeur (valeurs: Object = ) Object.assign (this, valeurs); export const Languages = ["Ruby", "Java", "JavaScript", "C", "Cpp"];
Nous avons défini une classe Pastebin et chaque instance de cette classe aura les propriétés suivantes:
identifiant
Titre
la langue
coller
Créez un autre fichier appelé pastebin.spec.ts
pour la suite de tests.
/ * pastebin.spec.ts * / // importer la classe Pastebin importer Pastebin depuis './pastebin'; describe ('Pastebin', () => it ('devrait créer une instance de Pastebin', () => expect (new Pastebin ()).. toBeTruthy ();)
La suite de tests commence par un décrire
block, qui est une fonction globale de Jasmine qui accepte deux paramètres. Le premier paramètre est le titre de la suite de tests et le second est son implémentation réelle. Les spécifications sont définies à l'aide d'un il
fonction qui prend deux paramètres, similaire à celui de la décrire
bloc.
Plusieurs spécifications (il
blocs) peuvent être imbriqués dans une suite de tests (décrire
bloc). Cependant, assurez-vous que les titres des suites de tests sont nommés de manière à ce qu'ils soient non ambigus et plus lisibles, car ils sont destinés à servir de documentation au lecteur..
Les attentes, mises en œuvre à l'aide du attendre
fonction, sont utilisés par Jasmine pour déterminer si une spécification doit réussir ou échouer. le attendre
function prend un paramètre appelé valeur réelle. Il est ensuite chaîné avec une autre fonction prenant la valeur attendue. Ces fonctions sont appelées fonctions de correspondance, et nous utiliserons les fonctions de correspondance comme toBeTruthy ()
, à définir()
, être()
, et contenir()
beaucoup dans ce tutoriel.
expect (new Pastebin ()). toBeTruthy ();
Donc, avec ce code, nous avons créé une nouvelle instance de la classe Pastebin et nous nous attendons à ce qu'elle soit vraie. Ajoutons une autre spécification pour confirmer que le modèle Pastebin fonctionne comme prévu.
it ('devrait accepter les valeurs', () => let pastebin = new Pastebin (); pastebin = id: 111, titre: "Hello world", langue: "Ruby", coller: "print" Hello "', expect (pastebin.id) .toEqual (111); expect (pastebin.language) .toEqual ("Ruby"); expect (pastebin.paste) .toEqual ('print "Hello"';);
Nous avons instancié la classe Pastebin et ajouté quelques attentes à nos spécifications de test. Courir ng test
pour vérifier que tous les tests sont verts.
Générez un service à l'aide de la commande ci-dessous.
ng générer le service pastebin
PastebinService
hébergera la logique d'envoi de requêtes HTTP au serveur; Cependant, nous n'avons pas d'API de serveur pour l'application que nous construisons. Par conséquent, nous allons simuler la communication du serveur à l'aide d'un module appelé InMemoryWebApiModule..
Installer angular-in-memory-web-api
via npm:
npm installer angular-in-memory-web-api --save
Mettre à jour AppModule avec cette version.
/ * app.module.ts * / import BrowserModule from '@ angular / platform-browser'; importer NgModule de '@ angular / core'; // Les composants importent AppComponent depuis './app.component'; // Service pour import Pastebin PastebinService à partir de "./pastebin.service"; // Modules utilisés dans ce tutoriel import HttpModule from '@ angular / http'; // En mémoire Web api pour simuler une importation de serveur http InMemoryWebApiModule à partir de 'angular-in-memory-web-api'; import InMemoryDataService à partir de './in-memory-data.service'; @NgModule (declarations: [AppComponent,], importations: [BrowserModule, HttpModule, InMemoryWebApiModule.forRoot (InMemoryDataService)], fournisseurs: [PastebinService], bootstrap: [AppComponent], export class AppModule
Créé un InMemoryDataService
qui met en œuvre InMemoryDbService
.
/*in-memory-data.service.ts*/ import InMemoryDbService de 'angular-in-memory-web-api'; importer Pastebin de './pastebin'; La classe d'exportation InMemoryDataService implémente InMemoryDbService createDb () const pastebin: Pastebin [] = [id: 0, titre: "Bonjour le monde Ruby", langue: "Ruby", coller: 'met "Bonjour le monde"', id : 1, titre: "Hello world C", langue: "C", coller: 'printf ("Hello world");', id: 2, titre: "Hello world CPP", langue: "C ++", coller: 'cout<<"Hello world";', id: 3, title: "Hello world Javascript", language: "JavaScript", paste: 'console.log("Hello world")' ]; return pastebin;
Ici, coller
est un tableau d'échantillons de pâtes qui seront renvoyés ou mis à jour lorsque nous effectuons une action HTTP telle que http.get
ou http.post
.
/*pastebin.service.ts * / import Injectable à partir de '@ angular / core'; importer Pastebin de './pastebin'; importer Http, en-têtes de '@ angular / http'; importer 'rxjs / add / operator / toPromise'; @Injectable () export class PastebinService // Le projet utilise InMemoryWebApi pour gérer l'API du serveur. // Ici "api / pastebin" simule une URL de l'API du serveur private pastebinUrl = "api / pastebin"; en-têtes privés = nouveaux en-têtes ('Content-Type': "application / json"); constructeur (privé http: Http) // getPastebin () exécute http.get () et renvoie une promesse publique getPastebin (): Promisereturn this.http.get (this.pastebinUrl) .toPromise () .then (response => response.json (). data) .catch (this.handleError); private handleError (error: any): promesse console.error ('Une erreur est survenue', erreur); renvoyer Promise.reject (error.message || error);
le getPastebin ()
méthode effectue une demande HTTP.get et renvoie une promesse qui résout un tableau d'objets Pastebin renvoyés par le serveur.
Si vous obtenez un Pas de fournisseur pour HTTP Erreur lors de l'exécution d'une spécification, vous devez importer le HTTPModule dans le fichier de spécification concerné..
Les composants sont le bloc de construction le plus fondamental d'une interface utilisateur dans une application angulaire. Une application angulaire est un arbre de composants angulaires.
- Documentation angulaire
Comme souligné précédemment dans la section Présentation, nous allons travailler sur deux composants dans ce tutoriel: PastebinComponent
et AddPasteComponent
. Le composant Pastebin consiste en une structure de tableau qui répertorie tout le collage extrait du serveur. Le composant AddPaste contient la logique de création de nouveaux pâtes..
Allez-y et générez les composants en utilisant Angular-CLI.
ng composant g --spec = false Pastebin
le --spec = false
option indique à la CLI angulaire de ne pas créer de fichier de spécification. C'est parce que nous voulons écrire des tests unitaires pour les composants à partir de zéro. Créer un pastebin.component.spec.ts
déposer à l'intérieur du pastebin-composant dossier.
Voici le code pour pastebin.component.spec.ts
.
importer TestBed, ComponentFixture, async de '@ angular / core / testing'; importer DebugElement depuis '@ angular / core'; importer PastebinComponent de './pastebin.component'; import By from '@ angular / platform-browser'; importer Pastebin, Languages de '… / pastebin'; // Modules utilisés pour tester l'importation HttpModule de '@ angular / http'; describe ('PastebinComponent', () => // Déclarations typescript. let comp: PastebinComponent; let fixture: ComponentFixture; let de: DebugElement; let element: HTMLElement; laissez mockPaste: Pastebin []; // beforeEach est appelé une fois avant chaque bloc "it" d'un test. // Utilisez ceci pour configurer le composant, injecter des services, etc. beforeEach (() => TestBed.configureTestingModule (declarations: [PastebinComponent]], // déclare le composant de test importé: [HttpModule],); fixture = TestBed .createComponent (PastebinComponent); comp = fixture.componentInstance; de = fixture.debugElement.query (By.css ('. pastebin')); element = de.nativeElement;); )
Il se passe beaucoup de choses ici. Brisons-le et prenons un morceau à la fois. Dans le décrire
bloc, nous avons déclaré des variables, puis nous avons utilisé un avant chaque
une fonction. beforeEach ()
est une fonction globale fournie par Jasmine et, comme son nom l'indique, elle est invoquée une fois avant chaque spécification du décrire
bloc dans lequel il s'appelle.
TestBed.configureTestingModule (declarations: [PastebinComponent]], // déclare les importations du composant de test: [HttpModule],);
TestBed
La classe est une partie des utilitaires de test angulaire et crée un module de test similaire à celui de @NgModule
classe. En outre, vous pouvez configurer TestBed
en utilisant le configureTestingModule
méthode. Par exemple, vous pouvez créer un environnement de test pour votre projet qui émule l'application Angular réelle, puis extraire un composant de votre module d'application et le rattacher à ce module de test..
fixture = TestBed.createComponent (PastebinComponent); comp = fixture.componentInstance; de = fixture.debugElement.query (By.css ('div')); element = de.nativeElement;
De la documentation angulaire:
lecréerComposant
méthode retourne unComposantfixture
, un descripteur de l'environnement de test entourant le composant créé. Le projecteur fournit un accès à l’instance de composant elle-même et auDebugElement
, qui est un handle sur l'élément DOM du composant.
Comme mentionné ci-dessus, nous avons créé un appareil de la PastebinComponent
et ensuite utilisé cette fixture pour créer une instance du composant. Nous pouvons maintenant accéder aux propriétés et méthodes du composant dans nos tests en appelant comp.nom_propriété
. Étant donné que le projecteur donne également accès au debugElement
, nous pouvons maintenant interroger les éléments et les sélecteurs DOM.
Il y a un problème avec notre code auquel nous n'avons pas encore pensé. Notre composant a un modèle externe et un fichier CSS. Les récupérer et les lire à partir du système de fichiers est une activité asynchrone, contrairement au reste du code, qui est entièrement synchrone..
Angular vous offre une fonction appelée async ()
cela prend en charge tous les trucs asynchrones. Ce que async consiste à garder trace de toutes les tâches asynchrones qu'il contient, tout en nous cachant la complexité de l'exécution asynchrone. Donc, nous allons maintenant avoir deux fonctions beforeEach, une asynchrone beforeEach ()
et un synchrone beforeEach ()
.
/ * pastebin.component.spec.ts * / // beforeEach est appelé une fois avant chaque bloc "it" d'un test. // Utilisez ceci pour configurer le composant, injecter des services, etc. beforeEach (async (() => // async auparavant) est utilisé pour la compilation de modèles externes qui constituent une activité asynchrone TestBed.configureTestingModule (declarations: [PastebinComponent], © / déclare le composant de test importé: [HttpModule],) .compileComponents (); // compile template and css)); beforeEach (() => // Et voici la fonction asynchrone synchrone fixture = TestBed.createComponent (PastebinComponent); comp = fixture.componentInstance; de = fixture.debugElement.query (By.css ('. pastebin')); element = de.nativeElement;);
Nous n'avons pas encore écrit de spécifications de test. Cependant, c'est une bonne idée de créer un aperçu des spécifications à l'avance. L'image ci-dessous montre une conception approximative du composant Pastebin.
Nous devons écrire des tests avec les attentes suivantes.
pastebinService
est injecté dans le composant et ses méthodes sont accessibles.onInit ()
est appelé.Les trois premiers tests sont faciles à mettre en œuvre.
it ('devrait avoir un composant', () => expect (comp) .toBeTruthy ();); it ('devrait avoir un titre', () => comp.title = 'Application Pastebin'; fixture.detectChanges (); expect (element.textContent) .toContain (comp.title);) it ('devrait avoir une table pour afficher les pâtes ', () => expect (element.innerHTML) .toContain ("thead"); expect (element.innerHTML) .toContain ("tbody");)
Dans un environnement de test, Angular ne lie pas automatiquement les propriétés du composant avec les éléments de modèle. Vous devez appeler explicitement fixture.detectChanges ()
chaque fois que vous souhaitez lier une propriété de composant avec le modèle. L'exécution du test devrait générer une erreur, car nous n'avons pas encore déclaré la propriété title dans notre composant..
title: string = "Application Pastebin";
N'oubliez pas de mettre à jour le modèle avec une structure de table de base.
Titre
identifiant Titre La langue Code
Pour le reste des cas, nous devons injecter le Pastebinservice
et rédigez des tests portant sur l’interaction composant-service. Un vrai service peut appeler un serveur distant et l'injecter sous sa forme brute sera une tâche laborieuse et difficile.
Au lieu de cela, nous devrions écrire des tests qui déterminent si le composant interagit avec le service comme prévu. Nous allons ajouter des spécifications qui espionnent le pastebinService
et son getPastebin ()
méthode.
Tout d'abord, importez le PastebinService
dans notre suite de tests.
importer PastebinService de '… /pastebin.service';
Ensuite, ajoutez-le à la fournisseurs
tableau à l'intérieur TestBed.configureTestingModule ()
.
TestBed.configureTestingModule (declarations: [CreateSnippetComponent], fournisseurs: [PastebinService],);
Le code ci-dessous crée un espion Jasmine conçu pour suivre tous les appels du getPastebin ()
méthode et retourner une promesse qui résout immédiatement mockPaste
.
// Le vrai PastebinService est injecté dans le composant let pastebinService = fixture.debugElement.injector.get (PastebinService); mockPaste = [id: 1, titre: "Bonjour le monde", langue: "Ruby", coller: "met 'Bonjour'"]; spy = spyOn (pastebinService, 'getPastebin') .et.returnValue (Promise.resolve (mockPaste));
L’espion ne se soucie pas des détails d’implémentation du service réel, mais contourne tout appel au réel getPastebin ()
méthode. De plus, tous les appels à distance enfouis à l'intérieur getPastebin ()
sont ignorés par nos tests. Nous allons écrire des tests unitaires isolés pour les services angulaires dans la deuxième partie du didacticiel..
Ajoutez les tests suivants à pastebin.component.spec.ts
.
it ('ne devrait pas montrer le pastebin avant OnInit', () => this.tbody = element.querySelector ("tbody"); // Essayez ceci sans la méthode 'replace (\ s \ s + / g, ") et voir ce qui se passe attend (this.tbody.innerText.replace (/ \ s \ s + / g, ")). ToBe (" "," tbody devrait être vide "); expect (spy.calls.any ()). toBe (false, "Spy ne devrait pas encore être appelé");); it ('ne doit toujours pas afficher pastebin après l'initialisation du composant', () => fixture.detectChanges (); // le service getPastebin est asynchrone, mais le test n'est pas. expect (this.tbody.innerText.replace (/ \ s \ s + / g, ")). toBe (" ", 'tbody devrait toujours être vide'); expect (spy.calls.any ()). toBe (true, 'getPastebin devrait être appelé');); ('devrait afficher le pastebin après que la promesse getPastebin ait été résolue', async () => fixture.detectChanges (); fixture.whenStable (). then () => fixture.detectChanges (); expect (comp.pastebin). toEqual (jasmine.objectContaining (mockPaste)); expect (element.innerText.replace (/ \ s \ s + / g, ")). toContain (mockPaste [0] .title););)
Les deux premiers tests sont des tests synchrones. La première spécification vérifie si le innerText
du div
L'élément reste vide tant que le composant n'est pas initialisé. Le deuxième argument de la fonction matcher de Jasmine est facultatif et s'affiche en cas d'échec du test. Ceci est utile lorsque vous avez plusieurs déclarations expect dans une spécification..
Dans la seconde spécification, le composant est initialisé (car fixture.detectChanges ()
est appelé), et l’espion devrait également être appelé, mais le modèle ne doit pas être mis à jour. Même si l'espion retourne une promesse résolue, le mockPaste
n'est pas encore disponible. Il ne devrait pas être disponible sauf si le test est un test asynchrone.
Le troisième test utilise un async ()
fonction discutée précédemment pour exécuter le test dans une zone de test asynchrone. async ()
est utilisé pour faire un test synchrone asynchrone. fixture.whenStable ()
est appelée lorsque toutes les activités asynchrones en attente sont complétées, puis une deuxième série de fixture.detectChanges ()
est appelé pour mettre à jour le DOM avec les nouvelles valeurs. L’attente dans le test final garantit que notre DOM est mis à jour avec le mockPaste
valeurs.
Pour réussir les tests, nous devons mettre à jour notre pastebin.component.ts
avec le code suivant.
/*pastebin.component.ts*/ import Component, OnInit de '@ angular / core'; importer Pastebin de '… / pastebin'; importer PastebinService de '… /pastebin.service'; @Component (sélecteur: 'app-pastebin', templateUrl: './pastebin.component.html', styleUrls: ['./pastebin.component.css']), la classe d'exportation PastebinComponent implémente OnInit title: string = " Application Pastebin "; pastebin: any = []; constructeur (public pastebinServ: PastebinService) // loadPastebin () est appelé le init ngOnInit () this.loadPastebin (); public loadPastebin () // appelle la méthode getPastebin () du service pastebin et stocke la réponse dans la propriété 'pastebin' this.pastebinServ.getPastebin (). then (pastebin => this.pastebin = pastebin);
Le modèle doit également être mis à jour.
Titre
identifiant Titre La langue Code paste.id paste.title paste.language Voir le code
Générez un composant AddPaste à l'aide de Angular-CLI. L'image ci-dessous illustre la conception du composant AddPaste.
La logique du composant doit passer les spécifications suivantes.
showModal
propriété à vrai
. (showModal
est une propriété booléenne qui devient vraie lorsque le modal est affiché et fausse quand le modal est fermé.)addPaste ()
méthode.showModal
propriété à faux
.Nous avons élaboré les trois premiers tests pour vous. Voyez si vous pouvez faire passer les tests vous-même.
describe ('AddPasteComponent', () => composant de let: AddPasteComponent; let fixture: ComponentFixture; let de: DebugElement; let element: HTMLElement; laisser espionner: jasmine.Espion; let pastebinService: PastebinService; beforeEach (async (() => TestBed.configureTestingModule (declarations: [AddPasteComponent], importations), importations: [HttpModule, FormsModule], fournisseurs: [PastebinService],) .compileComponents ();); beforeEach (() => // fixture d'initialisation = TestBed.createComponent (AddPasteComponent)); pastebinService = fixture.debugElement.injector.get (PastebinService); Component = fixture.componentInstance; '.add-paste')); element = de.nativeElement; spy = spyOn (pastebinService, 'addPaste'). and.callThrough (); // demande à un appareil de détecter les modifications fixture.detectChanges ();); it ('devrait être créé', () => expect (composant) .toBeTruthy ();); it ('devrait afficher le bouton' Créer un collage '', () => // Il devrait y avoir un bouton de création dans le modèle attendu (element.innerText) .toContain ("créer un collage");); it ('ne devrait pas afficher le modal à moins que le bouton ne soit cliqué', () => // source-model est un identifiant pour le modal. Il ne devrait pas s'afficher à moins que le bouton créer ne soit cliqué comme prévu (element.innerHTML). not.toContain ("source-modal");) it ("devrait afficher le modal lorsque vous cliquez sur" Créer le collage "", () => let createPasteButton = fixture.debugElement.query (By.css ("button" )); // triggerEventHandler simule un événement click sur l'objet bouton createPasteButton.triggerEventHandler ("click", null); fixture.detectChanges (); expect (element.innerHTML) .toContain ("source-modal"); expect (composant) .showModal) .toBeTruthy ("showModal devrait être vrai");))
DebugElement.triggerEventHandler ()
est la seule chose de nouveau ici. Il est utilisé pour déclencher un événement click sur l'élément button sur lequel il est appelé. Le deuxième paramètre est l’objet événement, et nous l’avons laissé vide puisque le composant Cliquez sur()
n'en attend pas un.
C'est tout pour la journée. Dans ce premier article, nous avons appris:
Dans le prochain didacticiel, nous allons créer de nouveaux composants, écrire d'autres composants de test avec des entrées et des sorties, des services et des itinéraires. Restez à l'écoute pour la deuxième partie de la série. Partagez vos pensées à travers les commentaires.