Angular 2 Jasmine 失败 undefined 不是一个对象

Angular 2 Jasmine failing undefined is not an object

我正在为 Angular 中的 NavBar 组件编写单元测试 2. 我模拟了路由器,它通过了我所有的测试;但是,我创建了一个服务来为路由设置活动 class,这导致我的单元测试失败并出现错误:

undefined is not an object (evaluating 'this.router.url.substring')

NavActive 服务

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';
        }
    }
}

导航栏组件

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;
    });

  }

}

导航栏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">
    &#9776;
  </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>

导航栏单元测试

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');
            });
        }
}

如何模拟 navActiveService 以便我的测试成功通过?

在您的 NavActiveService 中,您正在使用 this.router.url.substring(0,7),但您的 Router 存根没有 url 属性。它只有一个 navigate 函数。

也许对于每个测试,您可能只想设置 url 的值。

mockRouter.url = '/contact'

或者如果它不需要为测试而更改,只需在声明时添加它。

class MockRouter {
    url = '/';
    navigate = jasmine.createSpy('navigate');
}