37

In my Angular 4 component I have something like:

constructor(private route: ActivatedRoute) {
}

ngOnInit() {
  this.myId = this.route.snapshot.params['myId'];
}

And I'm trying to create a mock that suppose to look like follows:

class MockActivatedRoute extends ActivatedRoute {
  public params = Observable.of({ myId: 123 });
}    

My test fails with:

TypeError: Cannot read property 'params' of undefined.

How do I suppose to mock it? Have I misunderstood the proper usage of ActivatedRoute and should better use router.subscribe in my component? I saw some complicated examples where people mocked snapshot itself, however for me it looks overcomplicated.


The test itself is quite simple:

describe('ngOnInit', () => {
it('should set up initial state properly',
  () => {
  const component = TestBed.createComponent(MyComponent).componentInstance;
    component.ngOnInit();
    expect(component.myId).toEqual('123');
  });
});

If I simply change a method under the test to something like follows - the test works:

ngOnInit() {
    //this.myId = this.route.snapshot.params['myId'];
    this.route.params.subscribe(params => {
    this.myId = params['myId'];
    });
}

Obviously I need to mock Activated snapshot, but is there a better approach?

Kamil Naja
  • 6,267
  • 6
  • 33
  • 47
and85
  • 1,171
  • 1
  • 9
  • 12
  • I also tried to do something like `const fakeRoutes: Routes = [{path: 'info', data: { catalogId: '123' }, component: StatusComponent},]` and then `RouterTestingModule.withRoutes(fakeRoutes)`. Not sure if this syntax is supported trhough. – and85 Oct 19 '17 at 14:02
  • Are you trying to use this in unit testing? If so can you post that code – LLai Oct 19 '17 at 14:08
  • You are getting params of undefined because your mock is mocking the params observable `this.route.params` (returns an observable) instead of the snapshot `this.route.snapshot.params` (returns an object of the params) – LLai Oct 19 '17 at 15:01
  • Yeah, thanks. snapshot also uses params, so I thought it should be smart enough to understand that I mocked params. That means if I wanted to mocks both snapshot and params I would need to copy&paste params twice. Anyway, I've managed to resolve it yesterday, thanks for you help. – and85 Oct 20 '17 at 09:12

3 Answers3

49

Ok, I've found how to mock ActivatedRoute snapshot in the simple way. Something like this works for me:

providers: [MyComponent, {
  provide: ActivatedRoute,
  useValue: {snapshot: {params: {'myId': '123'}}}
}

Thanks :)

and85
  • 1,171
  • 1
  • 9
  • 12
39

If using route.snapshot.paramMap.get( 'uuid' ) instead:

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

{
    provide: ActivatedRoute, useValue:
        { snapshot: { paramMap: convertToParamMap( { 'uuid': '99-88-77' } ) } }
}
Simon
  • 2,994
  • 3
  • 28
  • 37
1

Instead of faking it, you can retrieve original route and simply spy on params

   let route: ActivatedRoute;

        beforeEach(() => {
          TestBed.configureTestingModule(...).compileComponents();

          route = TestBed.get(ActivatedRoute);
   })

   it("test",  () => {
      const spyRoute = spyOn(route.snapshot.paramMap, "get")
       spyRoute.and.callFake((paramNameToRead) => {

        switch (paramNameToRead) {
            case "id" : {
                return "value1";
            }
            case "foo" : {
                return null;
            }
            case "bar" : {
                return baz;
            }

        }


    });

}

walkeros
  • 4,736
  • 4
  • 35
  • 47