Reputation: 2922
I'm writing a unit test for my NavBar Component in Angular 2. I have the router mocked and it passes all of my tests; however, I created a service to set the active class for a route and it's causing my unit test to fail with the error:
undefined is not an object (evaluating 'this.router.url.substring')
NavActive Service
import { Injectable } from '@angular/core';
import { Router, RouterModule } from '@angular/router';
@Injectable()
export class NavActiveService {
constructor(private router: Router) {
}
homeActive() {
if (this.router.url === '/') {
return '#0fecdb';
}
else {
return '#00f';
}
}
loginActive() {
if (this.router.url.substring(0,6) === '/login') {
return '#0fecdb';
}
else {
return '#00f';
}
}
uploadActive() {
if (this.router.url.substring(0,7) === '/upload') {
return '#0fecdb';
}
else {
return '#00f';
}
}
aboutActive() {
if (this.router.url.substring(0,5) === '/about') {
return '#0fecdb';
}
else {
return '#00f';
}
}
contactActive() {
if (this.router.url.substring(0,7) === '/contact') {
return '#0fecdb';
}
else {
return '#00f';
}
}
four04Active() {
if (this.router.url === '/**') {
return '#0fecdb';
}
else {
return '#00f';
}
}
}
Navbar Component
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { NavActiveService } from '../../../services/navactive.service';
import { GlobalEventsManager } from '../../../services/GlobalEventsManager';
@Component({
moduleId: module.id,
selector: 'my-navbar',
templateUrl: 'navbar.component.html',
styleUrls:['navbar.component.css'],
providers: [NavActiveService]
})
export class NavComponent {
showNavBar: boolean = true;
constructor(private router: Router,
private navactiveservice:NavActiveService,
private globalEventsManager: GlobalEventsManager){
this.globalEventsManager.showNavBar.subscribe((mode:boolean)=>{
this.showNavBar = mode;
});
}
}
Navbar HTML
<div *ngIf="showNavBar">
<nav class="navbar navbar-fixed-top navbar-light bg-faded">
<button class="navbar-toggler hidden-sm-up" type="button" data-toggle="collapse" data-target="#navbar">
☰
</button>
<div class="collapse navbar-toggleable-xs" id="navbar">
<div class="container">
<a class="navbar-brand" [routerLink]="'/'">My App</a>
<ul class="nav navbar-nav navbar-right">
<li class="nav-item" >
<a class="nav-link" [routerLink]="'/'" [style.backgroundColor]="this.navactiveservice.homeActive()" > <i class="fa fa-home"></i> Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item" >
<a class="nav-link" [routerLink]="'/upload'" [style.backgroundColor]="this.navactiveservice.uploadActive()" > <i class="fa fa-upload"></i> Upload </a>
</li>
<li class="nav-item" >
<a class="nav-link" [routerLink]="'/about'" [style.backgroundColor]="this.navactiveservice.aboutActive()" > <i class="fa fa-bar-chart"> </i> Tracking Data</a>
</li>
<li class="nav-item" >
<a class="nav-link" [routerLink]="'/login'" [style.backgroundColor]="this.navactiveservice.loginActive()" > <i class="fa fa-user"></i> Logout</a>
</li>
</ul>
</div>
</div>
</nav>
</div>
Navbar Unit Test
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { NavComponent } from './navbar.component';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { RouterLinkStubDirective, RouterOutletStubComponent } from '../../../../test/router-stubs';
import { Router } from '@angular/router';
import { GlobalEventsManager } from '../../../services/GlobalEventsManager';
import { NavActiveService } from '../../../services/navactive.service';
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared.module';
export function main() {
let comp: NavComponent;
let fixture: ComponentFixture<NavComponent>;
let mockRouter:any;
class MockRouter {
//noinspection TypeScriptUnresolvedFunction
navigate = jasmine.createSpy('navigate');
}
describe('Navbar Componenet', () => {
beforeEach( async(() => {
mockRouter = new MockRouter();
TestBed.configureTestingModule({
imports: [ SharedModule ]
})
// Get rid of app's Router configuration otherwise many failures.
// Doing so removes Router declarations; add the Router stubs
.overrideModule(SharedModule, {
remove: {
imports: [ RouterModule ],
},
add: {
declarations: [ RouterLinkStubDirective, RouterOutletStubComponent ],
providers: [ { provide: Router, useValue: mockRouter }, GlobalEventsManager, NavActiveService ],
}
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(NavComponent);
comp = fixture.componentInstance;
});
}));
tests();
});
function tests() {
let links: RouterLinkStubDirective[];
let linkDes: DebugElement[];
beforeEach(() => {
// trigger initial data binding
fixture.detectChanges();
// find DebugElements with an attached RouterLinkStubDirective
linkDes = fixture.debugElement
.queryAll(By.directive(RouterLinkStubDirective));
// get the attached link directive instances using the DebugElement injectors
links = linkDes
.map(de => de.injector.get(RouterLinkStubDirective) as RouterLinkStubDirective);
});
it('can instantiate it', () => {
expect(comp).not.toBeNull();
});
it('can get RouterLinks from template', () => {
expect(links.length).toBe(5, 'should have 5 links');
expect(links[0].linkParams).toBe( '/', '1st link should go to Home');
expect(links[1].linkParams).toBe('/', '2nd link should go to Home');
});
it('can click upload link in template', () => {
const uploadLinkDe = linkDes[2];
const uploadLink = links[2];
expect(uploadLink.navigatedTo).toBeNull('link should not have navigated yet');
uploadLinkDe.triggerEventHandler('click', null);
fixture.detectChanges();
expect(uploadLink.navigatedTo).toBe('/upload');
});
}
}
How could I mock the navActiveService so that my tests pass successfully?
Upvotes: 3
Views: 3724
Reputation: 208984
In your NavActiveService
, you're using this.router.url.substring(0,7)
, but your Router
stub doesn't have a url
property. It only has a single navigate
function.
Maybe for each test you might just want to set the value for the url
.
mockRouter.url = '/contact'
Or if it doesn't need to change for the tests, just add it when you declare it.
class MockRouter {
url = '/';
navigate = jasmine.createSpy('navigate');
}
Upvotes: 1