web-dev-qa-db-fra.com

Comment simuler une Route parent activéeRoute dans angular2 à des fins de test?

Disons que j'ai ça

export class QuestionnaireQuestionsComponent {

    questions: Question[] = [];
    private loading:boolean = true;


    constructor(
        private route: ActivatedRoute,
        public questionnaireService:QuestionnaireService) {}

    ngOnInit(){
        this.route.parent.params.subscribe((params:any)=>{
            this.questionnaireService.getQuestionsForQuestionnaire(params.id).subscribe((questions)=>{
                this.questions = questions;
                this.loading = false;
            });
        });
    }


}

Mon composant fonctionne plutôt bien. Le problème est que je veux tester un peu mais je ne peux pas comprendre comment se moquer du this.route.parent objet. Voici mon test qui échoue

beforeEach(()=>{
    route = new ActivatedRoute();
    route.parent.params = Observable.of({id:"testId"});

    questionnaireService = jasmine.createSpyObj('QuestionnaireService', ['getQuestionsForQuestionnaire']);
    questionnaireService.getQuestionsForQuestionnaire.and.callFake(() => Observable.of(undefined));
    component = new QuestionnaireQuestionsComponent(route, questionnaireService);
});


describe("on init", ()=>{
    it("must call the service get questions for questionnaire",()=>{
        component.ngOnInit();
        expect(questionnaireService.getQuestionsForQuestionnaire).toHaveBeenCalled();
    });  
});

Le test échoue avec cette erreur

TypeError: undefined is not an object (evaluating 'this._routerState.parent') 
35
S.Galarneau

AcitvatedRoute est une interface selon angular2 docs , donc ce que j'ai fait est d'implémenter un MockActivatedRoute

import {Observable} from 'rxjs';
import {Type} from '@angular/core';
import {ActivatedRoute,Route,ActivatedRouteSnapshot,UrlSegment,Params,Data } from '@angular/router';

export class MockActivatedRoute implements ActivatedRoute{
    snapshot : ActivatedRouteSnapshot;
    url : Observable<UrlSegment[]>;
    params : Observable<Params>;
    queryParams : Observable<Params>;
    fragment : Observable<string>;
    data : Observable<Data>;
    outlet : string;
    component : Type<any>|string;
    routeConfig : Route;
    root : ActivatedRoute;
    parent : ActivatedRoute;
    firstChild : ActivatedRoute;
    children : ActivatedRoute[];
    pathFromRoot : ActivatedRoute[];
    toString() : string{
        return "";
    };
}

et remplacez simplement le ActivatedRoute dans mes tests pour MockActivatedRoute comme ceci

beforeEach(()=>{
    route = new MockActivatedRoute();
    route.parent = new MockActivatedRoute();
    route.parent.params = Observable.of({id:"testId"});

    questionnaireService = jasmine.createSpyObj('QuestionnaireService', ['getQuestionsForQuestionnaire']);
    questionnaireService.getQuestionsForQuestionnaire.and.callFake(() => Observable.of(undefined));
    component = new QuestionnaireQuestionsComponent(route, questionnaireService);
});
20
S.Galarneau

Utiliser TestBed

 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [YourComponent],
      imports: [],
      providers: [
        {
          provide: ActivatedRoute, useValue: {
            params: Observable.of({ id: 'test' })
          }
        }
      ]
    })
      .compileComponents();
  }));
41
Kevin Black

Pour ceux qui découvrent cette question pour la première fois, les documents angular.io couvrent ce scénario. Voir ici

Selon la documentation ci-dessus:

Créer la classe ActivatedRouteStub à utiliser comme double test pour ActivatedRoute

import { ReplaySubject } from 'rxjs/ReplaySubject';
import { convertToParamMap, ParamMap, Params } from '@angular/router';

/**
 * An ActivateRoute test double with a `paramMap` observable.
 * Use the `setParamMap()` method to add the next `paramMap` value.
 */
export class ActivatedRouteStub {
  // Use a ReplaySubject to share previous values with subscribers
  // and pump new values into the `paramMap` observable
  private subject = new ReplaySubject<ParamMap>();

  constructor(initialParams?: Params) {
    this.setParamMap(initialParams);
  }

  /** The mock paramMap observable */
  readonly paramMap = this.subject.asObservable();

  /** Set the paramMap observables's next value */
  setParamMap(params?: Params) {
    this.subject.next(convertToParamMap(params));
  };
}

Et utilisez ensuite le stub dans la classe de test comme suit:

activatedRoute.setParamMap({ id: '1234' });
9
Garreth Golding

Vous pouvez aussi simple plass {params: {searchTerm: 'this is not me'}} as any) as ActivatedRouteSnapshot

code de détail

  (service.resolve(({params: {id: 'this is id'}} as any) as ActivatedRouteSnapshot,
    {} as RouterStateSnapshot)as Observable<xyzResolveData>)
    .subscribe((data) => {
      expect((data as xyzResolveData).results).toEqual(xyzData.results);
    });
1
Aniruddha Das