Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 6 - how to mock router.events url response in unit test

As the title indicates, I need to mock router.events in a unit test.

In my component, I doing some regex to grab the first instance of text between slashes in the url; e.g., /pdp/

  constructor(
    private route: ActivatedRoute,
    private router: Router,
  }

this.router.events.pipe(takeUntil(this.ngUnsubscribe$))
      .subscribe(route => {
        if (route instanceof NavigationEnd) {
        // debugger
          this.projectType = route.url.match(/[a-z-]+/)[0];
        }
      });

My unit tests error out when the component is being built: Cannot read property '0' of null. When I comment-in the debugger, route does not seem to be set correctly, yet I am setting it in the unit test itself. I have done it several different ways (inspired by this post: Mocking router.events.subscribe() Angular2 and others).

First attempt:

providers: [
        {
          provide: Router,
          useValue: {
            events: of(new NavigationEnd(0, '/pdp/project-details/4/edit', 'pdp/project-details/4/edit'))
          }
        },
        // ActivatedRoute also being mocked
        {
          provide: ActivatedRoute,
          useValue: {
            snapshot: { url: [{ path: 'new' }, { path: 'edit' }] },
            parent: {
              parent: {
                snapshot: {
                  url: [
                    { path: 'pdp' }
                  ]
                }
              }
            }
          }
        }
]

Second attempt (based on the above post):

class MockRouter {
  public events = of(new NavigationEnd(0, '/pdp/project-details/4/edit', '/pdp/project-details/4/edit'))
}

providers: [
        {
          provide: Router,
          useClass: MockRouter
        }
]

Third attempt (also based on above post):

class MockRouter {
  public ne = new NavigationEnd(0, '/pdp/project-details/4/edit', '/pdp/project-details/4/edit');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
}

providers: [
        {
          provide: Router,
          useClass: MockRouter
        }
]

Fourth attempt:

beforeEach(() => {
    spyOn((<any>component).router, 'events').and.returnValue(of(new NavigationEnd(0, '/pdp/project-details/4/edit', 'pdp/project-details/4/edit')))
...

Fifth attempt:

beforeEach(() => {
    spyOn(TestBed.get(Router), 'events').and.returnValue(of({ url:'/pdp/project-details/4/edit' }))
...

In all the above cases, route is not being set; the NavigationEnd object is equal to:

{ id: 1, url: "/", urlAfterRedirects: "/" }

Thoughts?

like image 824
kriskanya Avatar asked Oct 19 '18 23:10

kriskanya


2 Answers

It can be as simple as:

TestBed.configureTestingModule({
  imports: [RouterTestingModule]
}).compileComponents()
    
...
    
const event = new NavigationEnd(42, '/', '/');
(TestBed.inject(Router).events as Subject<Event>).next(event);
like image 108
daniel-sc Avatar answered Sep 26 '22 03:09

daniel-sc


Here is the answer I came up with. I think I just didn't extend the first approach (above) far enough:

import { of } from 'rxjs';
import { NavigationEnd, Router } from '@angular/router';

providers: [
    {
      provide: Router,
      useValue: {
        url: '/non-pdp/phases/8',
        events: of(new NavigationEnd(0, 'http://localhost:4200/#/non-pdp/phases/8', 'http://localhost:4200/#/non-pdp/phases/8')),
        navigate: jasmine.createSpy('navigate')
      }
    }
]
like image 23
kriskanya Avatar answered Sep 24 '22 03:09

kriskanya