Un outil puissant pour simplifier vos tests angulaires
Le spectateur vous aide à vous débarrasser de tous les grognements de passe-partout, vous laissant des tests unitaires lisibles, élégants et rationalisés.
✅ Prise en charge des tests de composants, directives et services angulaires
✅ Interrogation DOM facile
✅ API Clean pour déclencher des événements clavier / souris / tactile
✅ Tester ng-content
✅ Matchers de jasmin / plaisanterie personnalisés (Tohaveclass, tabedisabled ..)
✅ Prise en charge des tests de routage
✅ Prise en charge des tests HTTP
✅ Prise en charge intégrée pour les composants d'entrée
✅ Prise en charge intégrée pour les fournisseurs de composants
✅ Fournisseurs de moqueries automobiles
✅ fortement tapé
✅ Support de plaisanterie
Les parrainages aident le développement et la maintenance continus des bibliothèques Ngneat. Envisagez de demander à votre entreprise de parrainer Ngneat comme cœur pour son développement et son développement d'applications.
Élevez votre soutien en devenant un sponsor d'or et faites votre logo en bonne place sur notre ReadMe dans les 5 meilleurs référentiels.
Stimulez votre soutien en devenant un sponsor d'or et profitez des projecteurs avec votre logo en bonne place dans les 3 meilleurs référentiels de notre Readme.

Devenez un sponsor en bronze et obtenez votre logo sur notre lecture sur Github.
Caractéristiques
Table des matières
Installation
NPM
Fil
Test des composants
Vues différées imbriquées
Sélecteur de cordes
Sélecteur de type
Sélecteur DOM
Tester des éléments sélectionnés
Composants moqueurs
Test des modules angulaires à composant unique / directif
Événements personnalisés
Créateurs d'événements
API d'événements
Aiders de clavier
Aides à la souris
Requêtes
Vues différées
Test avec l'hôte
Composant hôte personnalisé
Test avec routage
Déclencher une navigation
Test d'intégration avec RouterTestingModule
Options de routage
Directives de test
Services de test
Options supplémentaires
Tester les tuyaux
Utilisation du composant hôte personnalisé
Fournisseurs moqueurs
Se moquer des dépendances d'Oninit
Dépend des constructeurs moqueurs
Soutien de la plaisanterie
Tester avec HTTP
Injections mondiales
Fournisseurs de composants
Matchants personnalisés
Schémas
Collection de schémas par défaut
SPECTATEUR ET SPECTEUR ET MEST REPOS REPO ET KARMA
Équipe de base
Contributeurs
npm install @ngneat/spectator --save-dev
yarn add @ngneat/spectator --dev
Créez une usine de composants en utilisant la fonction createComponentFactory() , en passant la classe de composants que vous souhaitez tester. Le createComponentFactory() renvoie une fonction qui créera un nouveau composant dans chaque bloc it :
import {spectateur, createComponentFactory} de '@ ngneat / spectateur'; import {ButtonComponent} de './button.component' ;Describe('ButtonComponent', () => {{
Laissez Spectator: Spectator <FutComponent>;
const CreateComponent = CreateComponentFactory (ButtonComponent);
AVANTEACH (() => Spectator = CreateComponent ());
il ('devrait avoir une classe de réussite par défaut', () => {attendre (spectator.query ('bouton')). tohaveclass ('succès');
});
it ('devrait définir le nom de classe en fonction de la [className] entrée', () => {spectator.setInput ('className', 'danger'); attendre (spectateur.query ('bouton')). tohaveclass ('' danger '); attendre (spectator.query (' bouton ')). not.tohaveclass (' succès ');
});}); La fonction createComponentFactory peut éventuellement prendre les options suivantes qui étendent les options de module de test angulaire de base:
const CreateComponent = CreateComponentFactory ({{
Composant: ButtonComponent,
importations: [],
Fournisseurs: [],
déclarations: [],
EntryComponents: [],
ComponentProviders: [], // remplacer les fournisseurs du composant
ComponentViewProviders: [], // remplacer les fournisseurs de vue du composant
Modules de survie: [], // Modules de remplacement
OverrideComponents: [], // remplace les composants en cas de test de composant autonome
Superraditectifs: [], //, remplacez les directives en cas de test de directive autonome
Overridepipes: [], // remplacer les tuyaux en cas de test de tuyau autonome
MACKS: [], // Provideurs qui seront automatiquement moqués
Composantmocks: [], // fournisseurs de composants qui seront automatiquement moqués
ComponentViewProvidersMocks: [], // Les fournisseurs de visualisation des composants qui seront automatiquement moqués
détectchanges: false, // par défaut est vrai
DeclateComponent: false, // par défaut est vrai
DisableAnimations: False, // par défaut est vrai
superficiel: vrai, // par défaut est faux
DeferBlockBehavior: DeferBlockBehavior // par défaut à DeferBlockBehavior.Playthrough}); La fonction createComponent() prend éventuellement les options suivantes:
it ('devrait ...', () => {
Spectator = CreateComponent ({// Le composant InputSprops: {Title: 'Click'}, // remplace les fournisseurs du composant // Notez que vous devez le déclarer une fois dans `CreateComponentFactory`Providers: [], // Il faut exécuter la détection de modification de modification (par défaut est vrai) DetectChanges: Faux
});
attendre (spectator.query ('bouton')). toHavetext ('click');}); En fournissant des options overrideComponents dans la portée de notre fonction createComponent() nous pouvons définir la façon de remplacer la composante autonome et ses dépendances
@Composant({
Sélecteur: `` app-standalone-with-import ',
modèle: `<div id =" standalone "> Composant autonome avec import! </div> <app-standalone-avec-dépendance> </ app-standalone-with-dependency>`,
Imports: [standalonecomponent withDependency],
standalone: true,}) classe d'exportation standalonewithImportScomponent {} @component ({{
Sélecteur: `` App-standalone-avec-dépression ',
modèle: `<div id =" standalonewithdependency "> composant autonome avec dépendance! </div>`,
standalone: true,}) classe d'exportation standaloncomponentwithdependency {
Constructeur (question publique: QueryService) {}} @ composant ({{
Sélecteur: `` App-standalone-avec-dépression ',
modèle: `<div id =" standalonewithdependency "> composant autonome avec dépendance à la dépôt de remplacement! </v>`,
Standalone: true,}) classe d'exportation mockstandaloncomponent withDependency {
Constructor () {}} it ('devrait ...', () => {
const Spectator = CreateHostFactory ({Component: StandalOnewithIMPortsComponent, Template: `<div> <app-standalone-with-import> </ app-standalone-with-import> </v>`, overdeComponents: [standalOnewithIMPortsComponent, {retire: overdeComponents: {importations: [standaloncomponentwithDependency]}, ajouter: {importations: [mockstandaloncomponentwithdependency]},},],],
});
attendre (host.Query ('# standalone')). toContainText ('Composant autonome avec import!');
attendre (host.Query ('# standalonewithdependency')). toContainText ('Composant autonome avec dépendance à la dépression!');}); La méthode createComponent() renvoie une instance de Spectator qui expose l'API suivante:
fixture - Le luminaire du composant testé
component - L'instance du composant testé
element - l'élément natif du composant testé
debugElement - L'élément de débogage du luminaire testé
flushEffects() - Fournit un wrapper pour TestBed.flushEffects()
inject() - fournit un wrapper pour TestBed.inject() :
const Service = Spectator.Inject (QueyRsservice); const FromComponentInjector = true; const service = Spectator.Inject (QuerryService, FromComponentInjector);
detectChanges() - Exécute DetectChanges sur l'élément / hôte testé:
Spectator.detectChanges ();
detectComponentChanges() - Exécute detectChanges sur le composant testé (pas sur l' host ). Vous aurez besoin de cette méthode dans de rares cas lorsque vous utilisez un host et que le composant testé est onPush , et vous souhaitez le forcer à exécuter un cycle de détection des changements.
Spectator.detectComponentChanges ();
setInput() - modifie la valeur d'un @input () du composant testé. La méthode fonctionne manuellement ngOnChanges avec SimpleChanges s'il existe.
it ('devrait ...', () => {
Spectator.SetInput («className», «danger»);
spectator.setInput ({className: 'danger'
});}); output - Renvoie un @Output () observable du composant testé:
it ('devrait émettre l'événement $ sur clic', () => {
Laissez la sortie;
Spectator.Output ('Click'). Abonnez-vous (résultat => (Output = Result));
spectator.component.onclick ({type: 'click'});
attendre (output) .toequal ({type: 'click'});}); tick(millis?: number) - Exécutez la fonction FAKEASYNC tick() et appelez detectChanges() :
Il ('devrait fonctionner avec Tick', FAKEASYNC (() => {
Spectator = CreateComponent (ZippyComponent);
Spectator.Component.update ();
attendre (spectator.component.updatedasync) .tobeFalSy ();
Spectator.Tick (6000);
attendre (spectator.component.updatedasync) .not.tobeFalSy ();})) Chacun des événements peut accepter un SpectatorElement qui peut être l'un des éléments suivants:
Type SpectatorElement = String | Élément | Débugement | ElementRef | Fenêtre | Document | DomSelector;
S'il n'est pas fourni, l'élément par défaut sera l'élément hôte du composant testé.
click() - déclenche un événement de clic:
Spectator.Click (SpectatorElement); Spectator.Click (ByText ('Element')); blur() - déclenche un événement flou:
Spectator.Blur (SpectatorElement); Spectator.Blur (ByText ('Element'));Notez que si vous utilisez le framework Jest, Blur () ne fonctionne que si l'élément est concentré. Détails.
focus() - déclenche un événement de mise au point:
Spectator.focus (spectatorElement); spectator.focus (byText ('élément')); typeInElement() - Simulation du typage de l'utilisateur:
Spectator.Typeinement (Value, SpectatorElement); Spectator.Typeinement (Value, ByText ('Element')); dispatchMouseEvent() - déclenche un événement de souris:
Spectator.DispatchMouseEvent (SpectatorElement, 'Mouseout'); Spectator.DispatchMouseEvent (SpectatorElement, 'Mouseout'), X, Y, Event); Spectator.DispatchMousseevent (Bytex ('Élément'), 'Mouseout', x, y, événement); dispatchKeyboardEvent() - déclenche un événement de clavier:
Spectator.DispatchKeyBoardEvent (SpectatorElement, 'Keyup', 'Escape'); Spectator.DispatchKeyBoardEvent (SpectatorElement, 'Keyup', {key: 'Escape', Keycode: 27}) Spectator.DispatchKeyBoardEvent (ByText ('Element'), 'Key ',' Escape '); spectateur.DispatchKeyBoardEvent (byText (' Element '),' Keyup ', {Key:' Escape ', Keycode: 27}) dispatchTouchEvent() - déclenche un événement tactile:
Spectator.DispatchTouchEvent (spectatorElement, type, x, y); spectator.dispatchTouchEvent (byText ('élément'), type, x, y);Vous pouvez déclencher des événements personnalisés (@Output () des composants enfants) en utilisant la méthode suivante:
Spectator.triggeReventHandler (MyChildComponent, 'MyCustomevent', 'EventValue'); Spectator.triggeReventHandler (MyChildComponent, 'MyCustomevent', 'EventValue', {Root: True}); Spectator.Triggery ',' eventValue '); spectator.triggerAventHandler (' app-child-composant ',' myCustomevent ',' eventValue ', {root: true});Dans le cas où vous souhaitez tester les événements indépendamment de n'importe quel modèle (par exemple dans les services de présentation), vous pouvez se replier sur les créateurs d'événements sous-jacents. Ils fournissent essentiellement la même signature sans l'élément précédent.
const keyboardEvent = CreateKeyBoardEvent ('Keyup', 'Arrowdown' / *, TargetElement * /); const MouseEvent = CreateMousEEvent ('Mouseout'); const TouchEvent = CreateTouchevent ('TouchMove'); const FakeEvent = CreateFakeEvent ('Input'); Spectator.KeyBoard.PresSsenter (); Spectator.KeyBoard.PressEscape (); Spectator.KeyBoard.PressTab (); Spectator.KeyBoard.PressBackspace (); Spectator.KeyBoard.PressKey ('A'); Spectator.Keyboard.PressKey (' ctrl.a '); spectator.keyboard.presskey (' ctrl.shift.a '); spectator.mouse.contextMenu ('. Selector'); spectator.mouse.dblclick ('. Selector'); Notez que chacune des méthodes ci-dessus exécutera également detectChanges() .
L'API Spectator comprend des méthodes pratiques pour interroger le DOM dans le cadre d'un test: query , queryAll , queryLast , queryHost et queryHostAll . Toutes les méthodes de requête sont polymorphes et vous permettent d'interroger en utilisant l'une des techniques suivantes.
Passez un sélecteur de chaîne (dans le même style que vous le feriez lorsque vous utilisez jQuery ou Document.QuerySelector) pour interroger les éléments qui correspondent à ce chemin dans le DOM. Cette méthode de requête est équivalente aux prédicats Angular par.css. Notez que les éléments HTML natifs seront retournés. Par exemple:
// Renvoie un seul htmlelementspectator.query ('div> ul.nav li: first-child'); // renvoie un tableau de tous les htmlelementsSpectator.QueryAl de correspondance ('div> ul.nav li'); // Document contextator.Query ('div', {root: true}); spectator.Query ('App-Child', {read: ChildServiceService}); Passez un type (comme un composant, une directive ou une classe de fournisseur) pour interroger les instances de ce type dans le DOM. Cela équivaut à un prédicat By.directive d'Angular. Vous pouvez éventuellement passer dans un deuxième paramètre pour lire un jeton d'injection spécifique à partir des injecteurs des éléments correspondants. Par exemple:
// Renvoie une seule instance de MyComponent (si présent) Spectator.Query (MyComponent); // Renvoie l'instance de «SomService» que l'on trouve dans l'instance de «MyComponent» qui existe dans le DOM (si présent) Spectator.Query (MyComponent » , {read: SomeService}); spectator.Query (myComponent, {read: elementRef}); host.QueryLast (childComponent); host.Queryall (childComponent);Spectator vous permet de demander des éléments à l'aide de sélecteurs inspirés de Dom-Testing-Library. Les sélecteurs disponibles sont:
Spectator.Query (superplaceHolder ('Veuillez saisir votre adresse e-mail')); Spectator.Query (ByValue ('By Value')); Spectator.Query (ByTitle ('by Title')); Spectator.Query (ByalTText ('par Texte alt ')); spectator.Query (byLabel (' by Label ')); Spectator.Query (byText (' by Text ')); Spectator.Query (ByText (' by Text ', {Selector:' #Some. Selector '})); Spectator.Query (ByTextContent (' By Text Content ', {Selector:' #Some .Selector '})); Spectator.Query (Byrole (' Checkbox ', {Checked: true})); La différence entre byText et byTextContent est que le premier ne correspond pas au texte à l'intérieur des éléments imbriqués.
Par exemple, dans ce suivant HTML byText('foobar', {selector: 'div'}) ne correspondra pas à la div suivante, mais byTextContent Will:
<div> <span> foo </span> <span> bar </span> </div>
Spectator vous permet de demander des éléments imbriqués dans un élément parent. Ceci est utile lorsque vous avez plusieurs instances du même composant sur la page et que vous souhaitez interroger pour les enfants dans un spécifique. Le sélecteur parent est un sélecteur de chaîne qui est utilisé pour trouver l'élément parent. Le sélecteur parent est passé comme le deuxième paramètre des méthodes de requête. Par exemple:
Spectator.Query (ChildComponent, {ParentsElector: '# Parent-composant-1'}); Spectator.Queryall (ChildComponent, {ParentsElector: '# Parent-composant-1'}); Spectator vous permet de tester facilement les éléments <select></select> et prend en charge Multi Select.
Exemple:
il ('devrait définir les options correctes sur multi-sélectionn', () => {
const select = Spectator.Query ('# test-multi-select') en tant que htmlSelectElement;
Spectator.SelectOption (SELECT, ['1', '2']);
attendre (select) .tohaveselectedOptions (['1', '2']);}); it ('devrait définir l'option correcte sur standard select', () => {
const select = Spectator.Query ('# test-single-select') comme htmlSelectElement;
Spectator.SelectOption (SELECT, '1');
attendre (select) .toHaveselectedOptions ('1');}); Il vous permet également de vérifier si votre gestionnaire d'événements change agit correctement pour chaque élément sélectionné. Vous pouvez désactiver cela si vous avez besoin de pré-définir des choix sans envoyer l'événement de changement.
API:
Spectator.SelectOption (SelectElement: htmlSelectElement, Options: String | String [] | htmLoptionElement | htmLoptionElement [], config: {emitevents: boolean} = {emitevents: true});Exemple:
it ('devrait envoyer un nombre correct d'événements de changement', () => {
const Onchangespy = spyon (spectator.component, 'handlechange');
const select = Spectator.Query ('# test-onchange-select') en tant que htmlSelectElement;
Spectator.SelectOption (SELECT, ['1', '2'], {emitevents: true});
attendre (sélectionner) .tohaveselectedOptions (['1', '2']);
attendre (onchangespy) .tohavebeencalledtimes (2);}); it ('ne devrait pas expédier le nombre correct d'événements de changement', () => {
const Onchangespy = spyon (spectator.component, 'handlechange');
const select = Spectator.Query ('# test-onchange-select') en tant que htmlSelectElement;
Spectator.SelectOption (SELECT, ['1', '2'], {emitevents: false});
attendre (sélectionner) .tohaveselectedOptions (['1', '2']);
attendre (onchangespy) .not.tohavebeendtimes (2);}); Vous pouvez également passer HTMLOptionElement (s) comme arguments à selectOption et le Matcher toHaveSelectedOptions . Ceci est particulièrement utile lorsque vous utilisez la liaison [ngValue] sur la <option> :
il ('devrait définir l'option correcte sur une seule sélection lors du passage de l'élément', () => {
const select = Spectator.Query ('# test-single-select-element') as htmlSelectElement;
Spectator.SelectOption (SELECT, Spectator.Query (ByText ('Two')) en tant que htmLoptionElement);
attendre (select) .tohaveselectedOptions (spectator.Query (byText ('Two')) comme htmLoptionElement);}); Si vous avez besoin de se moquer des composants, vous pouvez utiliser la bibliothèque NG-Mocks. Au lieu d'utiliser CUSTOM_ELEMENTS_SCHEMA , qui pourrait masquer certains problèmes et ne vous aidera pas à définir des entrées, des sorties, etc., ng-mocks se moquera automatiquement des entrées, des sorties, etc. pour vous.
Exemple:
import {CreateHostFactory} à partir de '@ ngneat / spectateur'; import {mockComponent} de 'ng-Mocks'; import {foocomponent} from './path/to/foo.component' ;const createhost = createhostfactory ({{{{{{{{
Composant: YourComponentTotest,
Déclarations: [MockComponent (FooComponent)
]});Les composants (ou directives) qui sont déclarés dans leur propre module peuvent être testés en définissant le module de composant dans la liste des importations de l'usine de composants avec le composant. Par exemple:
const CreateComponent = CreateComponentFactory ({{
Composant: ButtonComponent,
importations: [ButtonComponentModule],}); Lorsqu'il est utilisé comme celui-ci, cependant, Spectator ajoute en interne le composant ButtonComponent aux déclarations du nouveau module créé en interne. Par conséquent, vous verrez l'erreur suivante:
Type ButtonComponent is part of the declarations of 2 modules [...]
Il est possible de dire à Spectator de ne pas ajouter le composant aux déclarations du module interne et, à la place, à utiliser le module explicitement défini tel quel. Définissez simplement la propriété declareComponent des options d'usine sur false :
const CreateComponent = CreateComponentFactory ({{
Composant: ButtonComponent,
importations: [ButtonComponentModule],
DeclateComponent: false,}); Lors de l'utilisation de CreateDirectiveFactory, définissez la propriété declareDirective des options d'usine sur false :
const CreateDirective = CreatedIrectiveFactory ({{
Composant: HighlightComponent,
importations: [highlightcomponentmodule],
DÉCLARADIGENCE: FAUX,}); Le spectateur fournit une API pratique pour accéder aux vues différées ( @defer {} ).
Accédez au bloc de déférer souhaité à l'aide de la méthode spectator.deferBlock(optionalIndex) . Le paramètre optionalIndex est facultatif et vous permet de spécifier l'index du bloc de différence auquel vous souhaitez accéder.
Accéder au premier bloc de report : appelez simplement spectator.deferBlock() .
Accéder aux blocs de différence ultérieurs : utilisez l'indice correspondant comme argument. Par exemple, spectator.deferBlock(1) accède au deuxième bloc (indexation basée sur zéro).
Le spectator.deferBlock(optionalIndex) renvoie quatre méthodes pour rendre différents états du bloc de report spécifié:
renderComplete() - rend l'état complet du bloc de repères.
renderPlaceholder() - Rend l'état de l'espace réservé du bloc de repères.
renderLoading() - Rend l'état de chargement du bloc de report.
renderError() - rend l'état d'erreur du bloc de différence.
Exemple:
@Component ({Selector: 'App-CMP', modèle: `@Defer (sur la fenêtre) {<div> État complet du premier bloc de report </div> <! - État complet du parent ->} @PlaceHolder { <div> placeholder </div>} `,}) class dummyComponent {} const CreateComponent = CreateComponentFactory ({Component: DummyComponent, DeferBlockBehavior: DeferBlockBehavior.Manual,}); it ('devrait rendre l'état complet', async () = > {// Arrangement constator = createComponent (); Pour accéder aux états dans les blocs de différence imbriqués, appelez la méthode deferBlock qui se chaîn à la méthode de l'état du bloc retourné.
Exemple: accéder à l'état complet imbriqué:
// en supposant `spectateur.deferBlock (0) .RenderComplete ()` Rend les informations complètes du parent de déferter BlockConst ParentCompleTate = Await Spectator.DeFerblock (). Render completetestate = attendre parentCompleTtate.RenderComplete (). DeferBlock ();
Exemple complet :
@Component ({sélecteur: 'app-cmp', modèle: `@Defer (sur la fenêtre) {<v> État complet du premier bloc de report </div> <! - État complet du parent -> @defer {< Div> État complet du bloc de différence imbriqué </div> <! - État complet imbriqué ->}} @placeholder {<div> placeholder </div>} `,}) dmmycomponent {} const CreateComponent = CreateComponentFactory ( {Component: DummyComponent, DeferBlockBehavior: DeferBlockBehavior.Manual,}); it ('devrait rendre le premier état complet imbriqué', async () => {// arrange constateur = createComponent (); // Act // Rende État complet ParentCompleTtate = Await Spectator.DeFerblock (). RenderChath complete (); reporter le bloc ');});Tester un composant avec un composant hôte est une technique plus élégante et puissante pour tester votre composant. Il vous donne essentiellement la possibilité d'écrire vos tests de la même manière que vous écrivez votre code. Voyons-le en action:
import {CreateHostFactory, SpectatorHost} à partir de '@ ngneat / spectateur'; décrire ('zippyComponent', () => {
Laissez Spectator: SpectatorHost <ZippyComponent>;
const CreateHost = createHostFactory (zippyComponent);
it ('devrait afficher le titre de la propriété hôte', () => {spectateur = createHost (`<zippy [title] =" title "> </ippy>`, {hostprops: {title: 'spectateur est génial'} }); attendre (spectateur.Query ('. Zippy__Title')). ToHavetext ('Spectator est génial');
});
it ('devrait afficher le mot "close" si ouvert', () => {spectateur = createHost (`<zippy title =" title zippy "> contenu zippy </ippy>`); spectateur.click ('. zippy__Title' .
});}); La méthode hôte renvoie une instance de SpectatorHost qui étend Spectator avec l'API supplémentaire suivante:
hostFixture - Le match de l'hôte
hostComponent - L'instance de composant de l'hôte
hostElement - l'élément natif de l'hôte
hostDebugElement - l'élément de débogage du luminaire de l'hôte
setHostInput - Modifie la valeur d'un @Input() du composant hôte
queryHost - En savoir plus sur l'interrogation dans Spectator
queryHostAll - En savoir plus sur la requête dans Spectator
Définir les entrées directement sur un composant à l'aide setInput ou props n'est pas possible lors du test avec un composant hôte. Les entrées doivent être définies via hostProps ou setHostInput à la place et transmises dans votre composant dans le modèle.
Parfois, il est utile de passer votre propre implémentation d'hôte. Nous pouvons transmettre un composant hôte personnalisé à createHostFactory() qui remplacera celui par défaut:
@Component ({Selector: 'Custom-host', modèle: ''}) classe CustomHostComponent {
title = 'personnalisé hostcomponent';} décrire ('avec composant hôte personnalisé', function () {
Laissez Spectator: SpectatorHost <ZippyComponent, CustomHostComponent>;
const CreateHost = CreateHostFactory ({Component: ZippyComponent, hôte: CustomHostComponent
});
it ('devrait afficher le titre du composant hôte', () => {spectateur = createHost (`<zippy [title] =" title "> </izippy>`); attendre (spectateur.Query ('. Zippy__Title'))) .toHaveText («HostComponent personnalisé»);
});}); Pour les composants qui utilisent le routage, il existe une usine spéciale qui étend la par défaut et fournit un ActivatedRoute Stubbed afin que vous puissiez configurer des options de routage supplémentaires.
décrire ('productDetailsComponent', () => {
Laissez Spectator: SpectatorRouting <ProductDetailSComponent>;
const CreateComponent = CreaterUtingFactory ({Component: ProductDetailsComponent, Params: {productId: '3'}, Data: {Title: 'Some Title'}
});
AVANTEACH (() => Spectator = CreateComponent ());
Il ('devrait afficher le titre de données de route', () => {attendre (spectator.Query ('. Title')). ToHavetext ('Some Title');
});
Il ('devrait réagir aux changements de route', () => {spectator.setRouteParam ('productId', '5'); // votre test ici ...
});}); L'API SpectatorRouting comprend des méthodes pratiques pour mettre à jour l'itinéraire actuel:
Interface SpectatorRouting <c> étend le spectateur <c> {
/ ** * simule une navigation de route en mettant à jour les paramètres, les parryparams et les flux de données observables. * /
TriggerNavigation (Options ?: RouteOptions): void;
/ ** * met à jour les paramètres de route et déclenche une navigation de route. * /
setRouteParam (nom: chaîne, valeur: chaîne): void;
/ ** * met à jour les paramètres de requête de route et déclenche une navigation de route. * /
setRouTeQueryParam (nom: chaîne, valeur: chaîne): void;
/ ** * met à jour les données de route et déclenche une navigation de route. * /
setRouteData (nom: chaîne, valeur: tout): void;
/ ** * met à jour le fragment de route et déclenche une navigation de route. * /
setRouteFragment (fragment: chaîne | null): void;
/ ** * met à jour l'URL de route et déclenche une navigation de route. * /
setRouteur (URL: UrlSegment []): void;}RouterTestingModule Si vous définissez l'option stubsEnabled sur false , vous pouvez passer une configuration de routage réelle et configurer un test d'intégration à l'aide du RouterTestingModule d'Angular.
Notez que cela nécessite des promesses pour résoudre. Une façon de faire face à cela est de rendre votre test asynchronisé:
décrire ('Test d'intégration de routage', () => {
const CreateComponent = CreateringFactory ({Component: MyComponent, Declations: [AutreComponent], StubSenabled: False, Routes: [{path: '', composant: MyComponent}, {path: 'foo', composant: autrecomponent}]
});
Il ('devrait naviguer à l'aide de Router Link', async () => {const Spectator = CreateComponent (); // attendez que les promesses résolvent ... attendent Spectator.Fixture.WhenStable (); // Testez la route actuelle par affirmer le locationExpect (Spectator.Inject (emplacement) .path ()). tobe ('/'); // cliquez sur un lien de routeur promet de résoudre ... attendre Spectator.fixture.Whenstable (); // Testez la nouvelle route en affirmant le LocationExpect (Spectator.inject (emplacement) .path ()). Tobe ('/ foo');
});}); La fonction createRoutesFactory peut prendre les options suivantes, en plus des options de spectateur par défaut:
params : paramètres initiaux à utiliser dans ActivatedRoute Stub
queryParams : paramètres de requête initiale à utiliser dans ActivatedRoute Stub
data : données initiales à utiliser dans ActivatedRoute Stub
fragment : fragment initial à utiliser dans ActivatedRoute Stub
url : segments d'URL initiaux à utiliser dans un stub ActivatedRoute
root : la valeur de root pour le talon ActivatedRoute
parent : la valeur du parent pour le stub ActivatedRoute
children : la valeur des children pour le talon ActivatedRoute
firstChild : la valeur de firstChild pour le stub ActivatedRoute
stubsEnabled (par défaut: true ): Active plutôt le Stub ActivatedRoute , s'il est défini sur false il utilise à la place RouterTestingModule
routes : Si stubsEnabled est Routes sur RouterTestingModule
Il existe une usine de test spéciale pour les directives de test. Disons que nous avons la directive suivante:
@Directive ({sélecteur: '[Highlight]'}) class d'exportation HighlightDirective {
@Hostbinding ('style.background-color') backgroundColor: String;
@HostListener ('MouseOver')
onhover () {this.backgroundcolor = '# 000000';
}
@HostListener («Mouseout»)
onLeave () {this.backgroundColor = '#ffffff';
}}Voyons comment nous pouvons tester facilement les directives avec Spectator:
décrire ('highlightDirective', () => {
Soit Spectator: Spectatordirective <ImpeveilDDirective>;
const CreateDirective = CreatedIrectiveFactory (HighlightDirective);
AVANT ENQUÊTE () => {Spectator = CreateDirective (`<div highlight> Testing Highlight Directive </div>`);
});
Il ('devrait modifier la couleur d'arrière-plan', () => {Spectator.DispatchMouseEvent (Spectator.Element, 'MouseOver'); attendre (spectator.Element) .tohavestyle ({backgroundColor: 'rgba (0,0,0, 0.1 .
});
Il ('devrait obtenir l'instance', () => {constance constance = spectator.directive; attendre (instance) .tobedeFined ();
});}); Définir les entrées directement sur une directive à l'aide setInput ou props n'est pas possible. Les entrées doivent être définies via hostProps ou setHostInput à la place et transmises à votre directive dans le modèle.
L'exemple suivant montre comment tester un service avec Spectator:
import {CreateServiceFactory, SpectatorService} de '@ ngneat / spectateur'; import {authService} de 'auth.service.ts'; décrire ('AuthService', () => {{
Laissez Spectator: SpectatorService <AuthService>;
const CreateService = CreateServiceFactory (AuthService);
AVANTEACH (() => Spectator = CreateService ());
Il ('ne doit pas être enregistré dans', () => {attendre (spectator.service.isloggedIn ()). tobeFalSy ();
});}); La fonction createService() renvoie SpectatorService avec les propriétés suivantes:
service - Obtenez une instance du service
inject() - un proxy pour Angular TestBed.inject()
Il est également possible de passer un objet avec des options. Par exemple, lors du test d'un service, vous souhaitez souvent se moquer de ses dépendances, car nous nous concentrons sur le service testé.
Par exemple:
@Injectable () Export class AuthService {
Constructeur (privé Datesservice: DaSerService) {}
isLoggedIn () {if (this.DaSeService.isexpired ('TimeStamp')) {return false;} return true;
}} Dans ce cas, nous pouvons se moquer de la dépendance DateService .
import {CreateServiceFactory, SpectatorService} de '@ ngneat / spectateur'; import {authService} de 'auth.service.ts'; décrire ('AuthService', () => {{
Laissez Spectator: SpectatorService <AuthService>;
const CreateService = CreateServiceFactory ({service: authService, fournisseurs: [], entryComponents: [], simule: [daService]
});
AVANTEACH (() => Spectator = CreateService ());
Il ('doit être enregistré dans', () => {const daService = Spectator.Inject (daService); daService.isexpired.and.returnvalue (false); attendre (spectateur.service.isloggedIn ()). TobetThy ();
});});L'exemple suivant montre comment tester un tuyau avec le spectateur:
import {spectatorpipe, createPipeFactory} de '@ ngneat / spectateur'; import {statSService} de './stats.service' ;Import {sumpipe} de' ./sum.pipe' ;deScribe('Sumpipe ', () => {
Laissez Spectator: SpectatorPipe <sumpipe>;
const CreatePipe = CreatePipeFactory (Sumpipe);
it ('devrait résumer la liste donnée de nombres (modèle)', () => {spectator = createPipe (`{{[1, 2, 3] | sum}}`); attendre (spectator.Element) .toHavetext ('6');
});
Il ('devrait résumer la liste donnée de nombres (prop)', () => {spectateur = createPipe (`{{prop | sum}}`, {hostprops: {prop: [1, 2, 3]}} ); attendre (spectator.element) .tohavetext ('6');
});
Il ('devrait déléguer la sommation au service', () => {const sum = () => 42; const provider = {fournir: statSService, useValue: {sum}}; spectateur = createPipe (`{{prop | sum}} `, {hostProps: {prop: [2, 40]}, fournisseurs: [proviseur]}); attendre (spectator.element) .tohavetext ('42 ');
});}); La fonction createPipe() renvoie SpectatorPipe avec les propriétés suivantes:
hostComponent - instance du composant hôte
debugElement - L'élément de débogage du luminaire autour du composant hôte
element - l'élément natif du composant hôte
detectChanges() - Un proxy pour TestBed.fixture.detectChanges()
inject() - un proxy pour Angular TestBed.inject()
Définir les entrées directement sur un tuyau à l'aide setInput ou props n'est pas possible. Les entrées doivent être définies via hostProps ou setHostInput à la place et passer à votre tuyau dans le modèle.
L'exemple suivant illustre comment tester un tuyau à l'aide d'un composant hôte personnalisé:
import {composant, entrée} de '@ angular / core'; import {spectatorpipe, createPipeFactory} de '@ ngneat / spectator'; import {moyenpipe} de './average.pipe' ;impport {statSservice} de' ./stats .service '; @ composant ({
modèle: `<div> {{prop | avg}} </div> `}) classe CustomHostComponent {
@Input () Public Prop: Number [] = [1, 2, 3];} décrire ('moyenpype', () => {
Laissez Spectator: SpectatorPipe <SightPipe>;
const CreatePipe = CreatePipeFactory ({Pipe: Moyenne, hôte: CustomHostComponent
});
Il ('devrait calculer la moyenne d'une liste donnée de nombres', () => {spectateur = createPipe (); attendre (spectateur.Element) .toHaveText ('2');
});
Il ('devrait résulter de 0 lorsque la liste des nombres est vide', () => {spectator = createPipe ({hostProps: {prop: []}}); attendre (spectator.element) .toHaveText ('0');
});
il ('devrait déléguer le calcul au service', () => {const avg = () => 42; const provider = {fournir: statSService, useValue: {avg}}; spectateur = createPipe ({fournisseurs: [Provider provider ]}); attendre (spectator.element) .tohavetext ('42 ');
});});Pour chaque usine de spectateurs, nous pouvons facilement se moquer de tout fournisseur.
Chaque service que nous transmettons à la propriété mocks sera moqué à l'aide de la fonction mockProvider() . La fonction mockProvider() convertit chaque méthode en un espion du jasmin. (c'est-à-dire jasmine.createSpy() ).
Voici quelques-unes des méthodes qu'il expose:
datesservice.isexpired.and.callthrough (); daService.isexpired.and.callfake (() => faux); daService.isexpired.and.throwerror ('error'); daService.isexpired.andcallfake (() => faux) ; Cependant, si vous utilisez la plaisanterie comme cadre de test et que vous souhaitez utiliser son mécanisme de moquerie à la place, importez le mockProvider() de @ngneat/spectator/jest . Cela utilisera automatiquement la fonction jest.fn() pour créer une simulation de plaisanterie à la place.
mockProvider() n'inclut pas les propriétés. Dans le cas où vous avez besoin d'avoir des propriétés sur votre simulation, vous pouvez utiliser le 2ème argument:
const CreateService = CreateServiceFactory ({{
Service: AuthService,
Fournisseurs: [MockProvider (AUTHRSERVICE, {Name: 'Martin', Emitte: nouveau sujet (), moquettedMethod: () => 'Mocked'})
],});Si un composant s'appuie sur un service qui se moque de la méthode du cycle de vie ONInit, la détection de changement doit être désactivée jusqu'à ce que les services aient été injectés.
Pour configurer cela, modifiez la méthode createComponent pour que l'option detectChanges soit définie sur FALSE, puis appelez manuellement detectChanges sur le spectateur après avoir configuré les services injectés.
const CreateComponent = CreateComponentFactory ({{
Composant: WeatherDashboardComponent}); it ('devrait appeler l'API météo sur init', () => {
const Spectator = createComponent ({DetectChanges: false
});
const weatherservice = Spectator.inject (WeatherDataapi);
weatherservice.getweatherdata.Andreturn (de (MockweatherData));
Spectator.detectChanges ();
attendre (weatherservice.getweatherdata) .tohaveeencalled ();});Si un composant s'appuie sur un service se moquant de son constructeur, vous devez créer et configurer la simulation et fournir la simulation lors de la création du composant.
const CreateComponent = CreateComponentFactory ({{
Composant: WeatherDashboardComponent}); it ('devrait appeler l'API météo dans le constructeur', () => {
const weatherservice = createSpyObject (WeatherDataapi);
weatherservice.getweatherdata.Andreturn (de (MockweatherData));
Spectator = CreateComponent ({Providers: [{fournir: WeatherDataapi, useValue: Weatherservice}]
});
attendre (weatherservice.getweatherdata) .tohaveeencalled ();});Par défaut, Spectator utilise le jasmin pour créer des espions. Si vous utilisez à la place Jest comme Framework de test, vous pouvez laisser Spectator créer des espions compatibles en jest.
Importez simplement l'une des fonctions suivantes de @ngneat/spectator/jest (au lieu de @ ngneat / spectateur), et il utilisera la plaisanterie au lieu du jasmin. createComponentFactory() , createHostFactory() , createServiceFactory() , createHttpFactory() , mockProvider() .
Import {CreateServiceFactory, SpectatorService} de '@ ngneat / spectateur / jest'; import {authService} de './auth.service' ;impport {daService} de' ./date.service' ;Describe('AuthService ', () => {
Laissez Spectator: SpectatorService <AuthService>;
const CreateService = CreateServiceFactory ({service: authService, Mocks: [daService]
});
AVANTEACH (() => Spectator = CreateService ());
Il ('ne doit pas être enregistré dans', () => {const daService = Spectator.inject <DaService> (daService); daService.isexpired.mockreturnvalue (true); attendre (spectator.service.isloggedIn ()). toBeFalsey ( ));
});
Il ('doit être enregistré dans', () => {const daService = Spectator.inject <DaService> (daService); daService.isexpired.mockreturnvalue (false); attendre (spectator.service.isloggedIn ()). tobetruthy () ;
});}); Lorsque vous utilisez le schéma du composant, vous pouvez spécifier l'indicateur --jest Jest pour que les importations de plaisanterie utilisent. Afin de plaisanter importe la valeur par défaut, mettez à jour angular.json :
"Schémas": {"@ ngneat / spectateur: spectateur-composant": {"jest": true
}
}Spectator facilite les services de données, qui utilisent le module HTTP angulaire, beaucoup plus facile. Par exemple, disons que vous avez un service avec trois méthodes, on effectue un get, un post et on exécute des demandes simultanées:
classe d'exportation todosdataservice {
constructeur (privé httpclient: httpclient) {}
getTodos () {return this.httpclient.get ('api / todos');
}
PostTodo (id: numéro) {return this.httpclient.post ('api / todos', {id});
}
CollectTodos () {return Merge (this.httpclient.get ('/ api1 / todos'), this.httpclient.get ('/ api2 / todos'));
}}Le test du service ci-dessus devrait ressembler:
import {createhttpfactory, httpMethod} de '@ ngneat / spectateur'; import {todosdataservice} de './todos-data.service' ;decribe('httpclient test', () => {
Laissez Spectator: SpectatorHttp <TodosdataService>;
const CreateHttp = createHttpFactory (todosdataService);
AVANTEACH (() => Spectator = CreateHttp ());
it ('peut tester httpclient.get', () => {spectator.service.getTodos (). abonnement (); spectator.Expectone ('api / todos', httpMethod.get);
});
it ('peut tester httpclient.post', () => {spectator.service.postodo (1) .subscribe (); const req = spectator.expectone ('api / todos', httpMethod.post); attendre (req. request.body ['id']). toequal (1);
});
Il ('peut tester les demandes HTTP actuelles', () => {Spectator.Service.getTodos (). SubScribe (); const reqs = spectator.expectCurrent ([{url: '/ api1 / todos', méthode: httpMethod.get }, {Url: '/ api2 / todos', méthode: httpMethod.get}]); spectator.flushall (reqs, [{}, {}, {}]);
});}); Nous devons créer une usine HTTP en utilisant la fonction createHttpFactory() , en passant le service que vous souhaitez tester. Le createHttpFactory() renvoie une fonction qui peut être appelée pour obtenir une instance de SpectatorHttp avec les propriétés suivantes:
controller - Un proxy pour HttpTestingController angulaire
httpClient - un proxy pour HttpClient angulaire
service - L'instance de service
inject() - un proxy pour Angular TestBed.inject()
expectOne() - attendez-vous à ce qu'une seule demande a été faite qui correspond à l'URL donnée et à sa méthode, et retourne sa demande simulée
Il est possible de définir des injections qui seront disponibles pour chaque test sans avoir besoin de les redémarrer dans chaque test:
// test.tsimport {DefineGlobalsInjections} de '@ ngneat / spectateur'; import {translocomodule} de '@ ngneat / transloco'; DefineGlobalSInjections ({{{{{{{{{{{{
importations: [translocomodule],}); Veuillez noter que defineGlobalsInjections() doit être appelé avant le chargement des modules. Dans le test.ts angulaire par défaut. Cela signifie avant cette ligne:
context.Keys (). Map (contexte);
Par défaut, les fournisseurs de composants d'origine (par exemple les providers du @Component ) ne sont pas touchés.
Cependant, dans la plupart des cas, vous souhaitez accéder aux fournisseurs du composant dans votre test ou les remplacer par des simulations.
Par exemple:
@Composant({
modèle: '...',
Fournisseurs: [Fooservice]}) classe Foocomponent {
Constructeur (Private Fooservice: Fooservice} {}
// ...} Utilisez les componentProviders pour remplacer le fournisseur FooService :
const CreateComponent = CreateComponentFactory ({{
Composant: Foocomposant,
ComponentProviders: [{Fournir: Fooservice, useValue: SomethingElse}
]}) Ou se moquez du service en utilisant componentMocks :
const CreateComponent = CreateComponentFactory ({{
Composant: Foocomposant,
ComponentMocks: [Fooservice]}); Pour accéder au fournisseur, obtenez-le à partir de l'injecteur de composant à l'aide du paramètre fromComponentInjector :
Spectator.inject (Fooservice, true)
De la même manière, vous pouvez également remplacer les fournisseurs de vue de composants en utilisant le componentViewProviders et componentViewProvidersMocks .
Les mêmes règles s'appliquent également aux directives en utilisant les paramètres directiveProviders et directiveMocks .
attendre ('. Zippy__content'). Not.toExist (); attendre ('. Zippy__content'). Tohavelength (3); attendre ('. Zippy__Content'). Tohaveid ('Id'); attendre (Spectator.Query ('. Zippy ')). TohaveAttribute (' Id ',' Zippy '); attendre (Spectator.Query ('. Zippy ')). TohaveAttribute ({id:' Zippy '}); attendre (Spectator.Query ('. Checkbox ' )). tohaveproperty ('checked', true); attendre (spectator.query ('. img')). tohaveproperty ({src: 'actifs / myimg.jpg'}); attendre (spectateur.Query ('. Img' )). toContainProperty ({src: 'myimg.jpg'}); // Notez que ToHaveclass accepte les classes uniquement dans un ordre strict. Si l'ordre n'est pas pertinent, désactivez le mode strict manuellement.expect ('. Zippy__content'). Tohaveclass ('class'); attendre ('. Zippy__content'). Tohaveclass ('class-a, class-b'); attendre ('. zippy__content '). not.tohaveclass (' class-b, class-a '); attendre ('. zippy__content '). tohaveclass ([' class-a ',' class-b ']); attendre ('. zippy__content ' . tohaveclass ('class-a, class-b', {strict: false}); attendre ('. zippy__content'). tohaveclass ('class-b, class-a', {strict: false}); attendre ('. zippy__content '). tohaveclass ([' class-b ',' class-a '], {strict: false}); attendre ('. zippy__content '). tohaveclass ([' class-b ',' class-a '] , {strict: false}); // Notez que Tohavetext ne recherche que l'existence d'une chaîne, pas si la chaîne est exactement la même. Si vous souhaitez vérifier que la chaîne est complètement la même, utilisez TohaveExActText.// Notez que si vous souhaitez vérifier que la chaîne est complètement la même, mais a d'abord réduit, utilisez ToHaveExActTrimMedText.// notez que si vous transmettez plusieurs valeurs, Spectator vérifie le texte de chaque élément de tableau par rapport à l'index de l'élément trouvé. ']); attendre ('. zippy__content '). tohavetext ((text) => text.incluses (' .. ')); attendre ('. zippy__content '). ') .toContainText ([' Content a ',' Content B ']); attendre ('. Zippy__content '). TohaveExActText (' Content '); attendre ('. Zippy__content '). TohaveExActText ([«Contenu a», » Content b ']); attendre ('. Zippy__content '). ToHaveExactTrimMedText (' Content '); attendre ('. Zippy__content '). TohaveExactTrimMedText ([' Content a ',' Content B ']); attendre ('. Zippy__Content ' . (. . .Checkbox '). tabeChecked (); attendre ('. Checkbox '). ToBeinDeTeminate (); attendre ('. Button '). TEDISABLE (); attendre (' div '). TobeEmpty (); attendre (' div ') .TobeHidden (); attendre ('élément'). tabeselected (); // remarquez qu'en raison de restrictions dans la plaisanterie (sans appliquer la logique de mise en page réelle dans DOM virtuel), certains correspondances peuvent entraîner de faux positifs. Par exemple, la largeur et la hauteur définies sur 0Expect ('élément'). Tabevisible (); attendre ('entrée'). Tobefocus (); attendre ('div'). Tobematchedby ('. Js-something'); attendre (spectateur. Component.Object) .Tobepartial ({APROPERTY: 'AVALUE'}); attendre ('div'). TohavedeScenelant ('. Child'); attendre ('div'). TohavedeSceNantWithText ({Selector: '.Child', Text: Text: 'texte'});Générer les composants, le service et la directive avec des modèles de spécifications de spectateurs avec une CLI angulaire: (lorsque vous l'utilisez par défaut)
Composant
Spécifications par défaut: ng g cs dashrized-name
Spec avec un hôte: ng g cs dashrized-name --withHost=true
Spec avec un hôte personnalisé: ng g cs dashrized-name --withCustomHost=true
Service:
Spécifications par défaut: ng g ss dashrized-name
Spec pour tester le service de données HTTP: ng g ss dashrized-name --isDataService=true
Directif:
ng g ds dashrized-name
Pour utiliser spectator comme collection par défaut dans votre projet Angular CLI, ajoutez-le à votre angular.json :
ng config cli.defaultcollection @ ngneat / spectateur
Les schémas spectator étendent la collection par défaut @schematics/angular . Si vous souhaitez définir des défauts de défaut pour des schémas tels que la génération de composants avec un fichier SCSS, vous devez modifier le nom du package schématique de @schematics/angular à @ngneat/spectator dans angular.json :
"schémas": {"@ ngneat / spectateur: spectateur-composant": {"style": "scss"
}
}Les exemples dans le guide du développeur du Karma de Docs Angular ont été reproduits dans le spectateur et la plaisanterie. (Pour plus de commodité, c'est la version locale des exemples de karma.)
La version Spectator & Jest est accessible ici.
Netanel basal | Dirk Luijk | Ben Elliott |
Merci à ces gens merveilleux (clé emoji): ~~~~
I. Sinaï ? ? | Valentin Buryakov ? | Ben Grynhaus ? | Martin Nuc | Lars Gyrup Brink Nielsen ? | Andrew Grekov ? | Jeroen Zwartepoorte |
Oliver Schlegel | Rex ye ? | tchmura | Yoeri nijs | Anders Skarby | Gregor Woiwode | Alexander Shememetev ? |
Micro | Mehmet erim | Brett Eckert | Ismail Faizi | Maxime | Jonathan Bonnefoy | Colum Ferry |
Chris Cooper | Marc Scheib | dgsmith2 | dedwardstech ? | tamasfoldi ? | Paolo Caleffi | Toni Villena |
Itay oded | Guillaume de Jabrun | Anand Tiwary | Ales Doganoc | Zoltan | Vitalii Baziuk | Clementlemarc-Certua |
Yuriy Grunin | Andrey Chalkin | Steven Harris | Richard Sahrakorpi | Dominik Kremer | Mehmet Ozan Turhan | Vlad lashko |
William Tjondrosuharto | Chaz Gatian | Pavel Korobov | Enno Lohmann | Pawel Boguslawski | Tobias avec | Mateo Tibaquirá |
Ce projet suit les spécifications de tous les contributeurs. Contributions de toute nature bienvenue!