Let's say I have this
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; }); }); } }
My component is actually working pretty well. Problem is that I want to unit test it but I can't figure out how to mock the this.route.parent
object. Here's my test that fails
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(); }); });
The test fails with this error
TypeError: undefined is not an object (evaluating 'this._routerState.parent')
You can get it with TestBed. get(ActivatedRoute) in your it functions if you want to stock it in a variable. This way you can subscribe to your paramMap and retrieve multiple values - in this case testId and anotherId .
ActivatedRoutelink. Provides access to information about a route associated with a component that is loaded in an outlet. Use to traverse the RouterState tree and extract information from nodes.
Step 1: Import ActivatedRoute from Router module. import { ActivatedRoute } from '@angular/router'; Step 2: Inject ActivatedRoute in constructor. Now we have id in edit_quiz.
RouterState and ActivatedRoute are similar to their snapshot counterparts except that they expose all the values as observables, which are great for dealing with values changing over time. Any component instantiated by the router can inject its ActivatedRoute.
Using TestBed
beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [YourComponent], imports: [], providers: [ { provide: ActivatedRoute, useValue: { params: Observable.of({ id: 'test' }) } } ] }) .compileComponents(); }));
AcitvatedRoute is an interface according to angular2 docs, so what I did is implements a 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 ""; }; }
and just replace the ActivatedRoute
in my tests for MockActivatedRoute
like this
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); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With