0

I need help in order to write unit tests for router.events.subscribe. I used ActivationEnd to use snapshot object which has properties which I need for my application. No other events from router provide these so I have to stick to this ActivationEnd. But I did not get anything so far as to how to mock this snapshot object.

Below is my code snippet.

ngOnInit() {
    this.router.events.subscribe(e => {
      if (e instanceof ActivationEnd) {
        this.section = e.snapshot.data.title;
        this.dynamicSubNavLabel = e.snapshot.routeConfig?.data?.label;
      }
    });
  }

Can you please help me what could I do to achieve unit tests for this or any alternatives ?

Regards, Alpesh

Alpesh Prajapati
  • 1,593
  • 2
  • 18
  • 38

2 Answers2

0

I sometimes use a mix of jasmine.createSpyObj and json object maybe this will help you:

 const activationEndMock = {
    ...jasmine.createSpyObj(['']),
    snapshot: {
      data: {
        title: 'a title'
      },
      routeConfig: {
        data: {
          label: 'a label'
        }
      }
    }
  }
  const routerMock = {
    ...jasmine.createSpyObj<Router>(['']),
    events: of(activationEndMock)
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      providers: [{ provide: Router, useValue: routerMock }]
    });
    .........
  });

I'm not sure this will work because I didn't test it.

pred
  • 185
  • 1
  • 14
0

Let say, you have a component like this:

import { Component, OnInit } from '@angular/core';
import { ActivationEnd, Router } from '@angular/router';

@Component({
  selector: 'app-test-router',
  templateUrl: './test-router.component.html',
  styleUrls: ['./test-router.component.css']
})
export class TestRouterComponent implements OnInit {
  section = '';
  dynamicSubNavLabel = '';
  constructor(private router: Router) { }

  ngOnInit() {
    this.router.events.subscribe(e => {
      if (e instanceof ActivationEnd) {
        this.section = e.snapshot.data.title;
        this.dynamicSubNavLabel = e.snapshot.routeConfig?.data?.label;
      }
    });
  }

}

Because Router.events is a Subject<Event> already. We can do this:

  • We'll cast it to Subject<Event> then call next to emit our custom/mock ActivationEnd Event.
  • Casting our mock data to unknown first, then casting to ActivatedRouteSnapshot.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, ActivationEnd, Event, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { Subject } from 'rxjs';

import { TestRouterComponent } from './test-router.component';

describe('TestRouterComponent', () => {
  let component: TestRouterComponent;
  let fixture: ComponentFixture<TestRouterComponent>;
  let router: Router;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [ TestRouterComponent ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(TestRouterComponent);
    component = fixture.componentInstance;
    router = TestBed.inject(Router);
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should update data from router', () => {
    const events$ = router.events as Subject<Event>;
    const mockSnapshot = {
      data: {
        title: 'some heading'
      },
      routeConfig: {
        data: {
          label: 'some text'
        }
      }
    } as unknown as ActivatedRouteSnapshot;
    events$.next(new ActivationEnd(mockSnapshot));
    expect(component.section).toBeTruthy();
    expect(component.dynamicSubNavLabel).toBeTruthy();
  })
});

Source code from Angular: https://github.com/angular/angular/blob/75a3c778b1f7be913f0287423d40fade68ee9adc/packages/router/src/router.ts#L461

Tiep Phan
  • 12,386
  • 3
  • 38
  • 41
  • Great Tiep... It worked but I changed only one line to "const events$ = router.events as Subject;" instead of Event as it was throwing lot of errors... But you deserve bounty so thanks for your answer. :) – Alpesh Prajapati Sep 12 '21 at 12:43