Wie teste ich eine Komponente, die von den Parametern von ActivatedRoute abhängt?

91

Ich teste eine Komponente, die zum Bearbeiten eines Objekts verwendet wird. Das Objekt verfügt über eine eindeutige idEigenschaft, die verwendet wird, um das bestimmte Objekt aus einem Array von Objekten abzurufen, die in einem Dienst gehostet werden. Das Spezifische idwird über einen Parameter beschafft, der über das Routing übergeben wird, insbesondere über die ActivatedRouteKlasse.

Der Konstruktor ist wie folgt:

 constructor(private _router:Router, private _curRoute:ActivatedRoute, private _session:Session) {
}

ngOnInit() {
    this._curRoute.params.subscribe(params => {
        this.userId = params['id'];
        this.userObj = this._session.allUsers.filter(user => user.id.toString() === this.userId.toString())[0];

Ich möchte grundlegende Komponententests für diese Komponente durchführen. Ich bin mir jedoch nicht sicher, wie ich den idParameter einfügen kann, und die Komponente benötigt diesen Parameter.

Übrigens: Ich habe schon ein Mock für den SessionService, also keine Sorge.

Colby Cox
quelle

Antworten:

132

Der einfachste Weg, dies zu tun, besteht darin, einfach das useValueAttribut zu verwenden und ein Observable des Wertes bereitzustellen, den Sie verspotten möchten.

RxJS <6

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: Observable.of({id: 123})
  }
}

RxJS> = 6

import { of } from 'rxjs';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: of({id: 123})
  }
}
zmanc
quelle
11
Observable.of existiert für mich nicht! : S
Alejandro Sanz Díaz
4
Import Observable von rxjs / Observable
zmanc
6
Dieser Code macht diesen Fehler in meinem Projekt:Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/HomeContentComponent.ngfactory.js'. at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2605
MixerOID
5
RxJs 6 ofsollte alleine verwendet werden. Außerdem würden Sie wahrscheinlich RouterTestingModuleanstelle des Codes dieser Antwort verwenden.
Ben Racicot
5
@ BenRacicot Diese Antwort wurde gegeben, bevor RxJs 6 existierte. Wenn Sie stattdessen "stattdessen tun" sagen, erhalten Sie eine Antwort, die direkt bewertet werden kann.
Zmanc
18

Ich habe herausgefunden, wie das geht!

Da ActivatedRoutees sich um einen Dienst handelt, kann ein Scheindienst dafür eingerichtet werden. Nennen wir diesen Mock-Service MockActivatedRoute. Wir erweitern ActivatedRoutein MockActivatedRoute, wie folgt:

class MockActivatedRoute extends ActivatedRoute {
    constructor() {
        super(null, null, null, null, null);
        this.params = Observable.of({id: "5"});
    }

Die Zeile super(null, ....)initialisiert die Superklasse, die vier obligatorische Parameter hat. In diesem Fall benötigen wir jedoch nichts von diesen Parametern, daher initialisieren wir sie mit nullWerten. Alles was wir brauchen ist der Wert, der paramsein ist Observable<>. Daher this.paramsüberschreiben wir mit den Wert von paramsund initialisieren ihn so, dass er Observable<>der Parameter ist, auf den sich die Testperson verlässt.

Initialisieren Sie ihn dann wie jeden anderen Mock-Service einfach und überschreiben Sie den Anbieter für die Komponente.

Viel Glück!

Colby Cox
quelle
1
Ich stehe gerade vor diesem Problem! Ich erhalte jedoch Fehler, wenn ich versuche, superoder zu verwenden Observable. Woher kommen diese?
Aarmora
super()ist eingebaut. Observableist von rxjs/Observableoder nur rxjsabhängig von Ihrer Version. Sie würden es mit bekommen import {Observable} from 'rxjs'.
oooyaya
Sie haben eine Antwort akzeptiert und eine andere gepostet ... wenn dies Highlander war (und es nur eine geben konnte), welche haben Sie "wirklich" ausgewählt und warum? Das heißt, ich denke, dies reduziert sich im Wesentlichen auf das Gleiche wie die Antwort von zmanc, die Sie akzeptiert haben. Haben Sie zusätzlichen Wert durch die Einrichtung dieses [etwas] komplizierteren Modells gefunden?
Ruffin
9

Hier ist, wie ich es in Angular 2.0 getestet habe ...

import { ActivatedRoute, Data } from '@angular/router';

und im Abschnitt Anbieter

{
  provide: ActivatedRoute,
  useValue: {
    data: {
      subscribe: (fn: (value: Data) => void) => fn({
        yourData: 'yolo'
      })
    }
  }
}
Rady
quelle
Können Sie den vollständigen Code für den Anbieterbereich bereitstellen?
Michael JDI
Dies ist eine vollständige Unit-Test-Klasse. plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady
Wie beurteilen Sie abmelden in ngOnDestroy testen
shiva
Dies wird in einem realen Anwendungsfall unterbrochen, da Sie kein Abonnement zurückgeben und den Aufruf .unsubscribe () in ngOnDestroy nicht verwenden können.
Quovadisqc
1
data: Observable.of ({yourData: 'yolo'}) würde allerdings funktionieren.
Quovadisqc
8

In Winkel 8+ gibt es das RouterTestingModule, mit dem Sie auf die ActivatedRoute oder den Router der Komponente zugreifen können. Sie können auch Routen an das RouterTestingModule übergeben und Spione für die angeforderten Routenmethoden erstellen.

Zum Beispiel habe ich in meiner Komponente:

ngOnInit() {
    if (this.route.snapshot.paramMap.get('id')) this.editMode()
    this.titleService.setTitle(`${this.pageTitle} | ${TAB_SUFFIX}`)
}

Und in meinem Test habe ich:

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ProductLinePageComponent ],
      schemas: [NO_ERRORS_SCHEMA],
      imports: [
        RouterTestingModule.withRoutes([])
      ],
    })
    .compileComponents()
  }))

  beforeEach(() => {
    router = TestBed.get(Router)
    route = TestBed.get(ActivatedRoute)
  })

und später im Abschnitt 'es':

  it('should update', () => {
    const spyRoute = spyOn(route.snapshot.paramMap, 'get')
    spyRoute.and.returnValue('21')
    fixture = TestBed.createComponent(ProductLinePageComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
    expect(component).toBeTruthy()
    expect(component.pageTitle).toBe('Edit Product Line')
    expect(component.formTitle).toBe('Edit Product Line')
    // here you can test the functionality which is triggered by the snapshot
  })

In ähnlicher Weise können Sie die paramMap direkt über die spyOnProperty-Methode von Jasmin testen, indem Sie ein Observable zurückgeben oder rxjs-Murmeln verwenden. Dies kann einige Zeit sparen und erfordert auch keine zusätzliche Scheinklasse. Hoffe, dass es nützlich und sinnvoll ist.

dimitris maf
quelle
So viel besser, als ein zusätzliches Modell warten zu müssen, und Sie können in Tests problemlos verschiedene Parameter einstellen. Danke dir!
Migg
Das hilft. Wissen Sie, wie man verschiedene Parameter ausspioniert: const dirName = this.route.snapshot.paramMap.get ('dirName'); const actionType = this.route.snapshot.paramMap.get ('actionType'); Auf welchem ​​Bot wird spyOn ausspioniert (route.snapshot.paramMap, 'get')? Kann ich einen Schlüssel zum Abhören angeben?
Speksy
Wie oben erwähnt, können Sie spyOnProperty anstelle von spyOn verwenden, z. B. spyOnProperty (route.snapshot.paramMap.get, 'dirName'). Wenn ich Ihre Frage nicht vollständig beantwortet habe, zögern Sie nicht, es mir zu sagen. Vielen Dank.
Dimitris Maf
4

Fügen Sie einfach einen Mock der ActivatedRoute hinzu:

providers: [
  { provide: ActivatedRoute, useClass: MockActivatedRoute }
]

...

class MockActivatedRoute {
  // here you can add your mock objects, like snapshot or parent or whatever
  // example:
  parent = {
    snapshot: {data: {title: 'myTitle ' } },
    routeConfig: { children: { filter: () => {} } }
  };
}
Francesco Borzi
quelle
3

Für einige Leute, die an Angular> 5 arbeiten, wenn Observable.of (); funktioniert nicht, dann können sie nur von () verwenden, indem sie import {of} aus 'rxjs' importieren;

Shashank Sharma
quelle
1

Beim Erstellen von Testsuiten für einen Routing-Pfad ist dasselbe Problem aufgetreten wie:

{
   path: 'edit/:property/:someId',
   component: YourComponent,
   resolve: {
       yourResolvedValue: YourResolver
   }
}

In der Komponente habe ich die übergebene Eigenschaft wie folgt initialisiert:

ngOnInit(): void {    
   this.property = this.activatedRoute.snapshot.params.property;
   ...
}

Wenn Sie beim Ausführen der Tests keinen Eigenschaftswert in Ihrer aktivierten ActivatedRoute "useValue" übergeben, werden Sie undefiniert, wenn Sie Änderungen mit "fixture.detectChanges ()" erkennen. Dies liegt daran, dass die Scheinwerte für ActivatedRoute nicht die Eigenschaft params.property enthalten. Dann muss der Mock useValue über diese Parameter verfügen, damit das Fixture die Eigenschaft 'this.property' in der Komponente initialisieren kann. Sie können es hinzufügen als:

  let fixture: ComponentFixture<YourComponent>;
  let component: YourComponent;
  let activatedRoute: ActivatedRoute; 

  beforeEach(done => {
        TestBed.configureTestingModule({
          declarations: [YourComponent],
          imports: [ YourImportedModules ],
          providers: [
            YourRequiredServices,
            {
              provide: ActivatedRoute,
              useValue: {
                snapshot: {
                  params: {
                    property: 'yourProperty',
                    someId: someId
                  },
                  data: {
                    yourResolvedValue: { data: mockResolvedData() }
                  }
                }
              }
            }
          ]
        })
          .compileComponents()
          .then(() => {
            fixture = TestBed.createComponent(YourComponent);
            component = fixture.debugElement.componentInstance;
            activatedRoute = TestBed.get(ActivatedRoute);
            fixture.detectChanges();
            done();
          });
      });

Sie können mit dem Testen beginnen, wie zum Beispiel:

it('should ensure property param is yourProperty', async () => {
   expect(activatedRoute.snapshot.params.property).toEqual('yourProperty');
   ....
});

Angenommen, Sie möchten einen anderen Eigenschaftswert testen, dann können Sie Ihre nachgebildete ActivatedRoute wie folgt aktualisieren:

  it('should ensure property param is newProperty', async () => {
    activatedRoute.snapshot.params.property = 'newProperty';
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.debugElement.componentInstance;
    activatedRoute = TestBed.get(ActivatedRoute);
    fixture.detectChanges();

    expect(activatedRoute.snapshot.params.property).toEqual('newProperty');
});

Hoffe das hilft!

Diego Herrera
quelle
0

Anbieter in der Testklasse hinzugefügt als:

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
Adelinor
quelle