Angular2 RC.1 - 将路由器注入单元测试

时间:2016-05-20 22:08:13

标签: angular jasmine angular2-routing

我正在编写我的ng2测试,而且我在测试时将Router注入我的组件时遇到了一些麻烦。我的组件的构造函数只接受一个参数 - private router: Router

但是,当我运行我的测试用例时,我在尝试注入路由器时遇到错误。我究竟做错了什么?任何人都可以提供一个有效的例子吗?

我使用angular2-RC.1

这是我得到的错误: No provider for ComponentResolver! (Router -> ComponentResolver)

这是我的测试:

import {describe, it, expect, beforeEach, afterEach, beforeEachProviders, inject} from "@angular/core/testing";
import {ReflectiveInjector, provide} from "@angular/core";
import {HTTP_PROVIDERS} from "@angular/http";
import {Router, ROUTER_PROVIDERS} from "@angular/router";
import {ROUTER_FAKE_PROVIDERS} from "@angular/router/testing";
import {Location} from "@angular/common";
import {SpyLocation} from "@angular/common/testing/location_mock";
import {Observable} from "rxjs/Observable";
import {MyComp} from "./MyComp";

describe("MyComp", () => {

  let injector: ReflectiveInjector,
      myComp: MyComp,
      router: Router;

  beforeEach(() => {

    injector = ReflectiveInjector.resolveAndCreate([         
      HTTP_PROVIDERS,
      ROUTER_FAKE_PROVIDERS,          
      provide(Location, {useClass: SpyLocation})

    ]);

    router = injector.get(Router);
    myComp = new MyComp(router);


  });    

  afterEach(() => {
    injector = null;    
    myComp = null;
    router = null; 
  });

  it("should be defined", () => {

    expect(myComp).toBeDefined();

  });



});

6 个答案:

答案 0 :(得分:10)

您需要为路由器创建一个jasmine间谍对象。 AppComponent有一个接收路由器的构造函数。

import { AppComponent } from './app.component';
import {Router, ROUTER_PROVIDERS} from "@angular/router";

describe('app component', () => {
    let component: AppComponent;
    let router: Router;
    beforeAll(() => {
        router = jasmine.createSpyObj("Router", ['navigate']);
        component = new AppComponent(router);
    });
    it("should be defined", () => {
        expect(component).toBeDefined();
    });
});

答案 1 :(得分:2)

使用匿名ES 6课程和jasmine

beforeEach(() => addProviders([
{ 
    provide: Router, 
    useClass: class { navigate = jasmine.createSpy("navigate"); }
}]));

答案 2 :(得分:1)

一些修改......这个简单的解决方案对我有用:

import {MyComp} from "./MyComp";
import {RootRouter} from 'angular2/src/router/router';
import {provide} from 'angular2/core'; 
import {Router} from 'angular2/router';

describe("MyComp", () => {
  let myComp: MyComp,
  router;

beforeEach(() => {
  provide(Router, {useClass: RootRouter})
  myComp = new MyComp(router);
}) 

it("should be defined", () => {
  expect(myComp).toBeDefined();
  });
});

答案 3 :(得分:0)

好吧,我想出了一个解决方案。它并不理想,但它有效。基本上,我正在创建一个实现我需要的方法的MockRouter类。

<强> MockRouter:

export class MockRouter {

    public navigate() {
        console.log(“Mock router was called.”);
    }

}

现在,在我的测试用例中,我所要做的就是为路由器提供模拟实现:

provide(Router, {useClass: MockRouter})

如果NG2文档显示如何正确地将Router注入到Jasmine测试用例中,那么真的会很好。嘲弄他们的物体似乎应该是一个不必要的步骤。

(仅供参考,我尝试使用ROUTER_FAKE_PROVIDERS并仍然遇到上述ComponentResolver错误

答案 4 :(得分:0)

这是另一种解决方案,但有点冗长 允许我们使用 SpyLocation 来检查路线变化。 首先,我们创建通用测试路由器提供商。

路由器 - 测试 - providers.ts

import { ComponentResolver } from '@angular/core';
import { Type } from '@angular/core/src/facade/lang';
import { SpyLocation } from '@angular/common/testing';
import { Location } from '@angular/common';
import { Router, RouterOutletMap } from '@angular/router';
import { RouteSegment } from '@angular/router/src/segments';
import { RouterUrlSerializer, DefaultRouterUrlSerializer } from '@angular/router/src/router_url_serializer';
/**
 * this class provides the means of loading the tested component type
 */
export class FakeRootComponentLoader {
    constructor(private rootComponentType: Type) {
      this.rootComponentType = rootComponentType;
    }
    public getRootComponentType = () => {
        return this.rootComponentType;
    }
 }

let routerFactory = function (
        fakeRootComponentLoader: FakeRootComponentLoader,
        componentResolver: ComponentResolver,
        urlSerializer: RouterUrlSerializer,
        routerOutletMap: RouterOutletMap,
        location: Location): Router
{
  let fakeRootComponentType = fakeRootComponentLoader.getRootComponentType();
  /**
   * _rootComponent should not be null, but it is what in angular2 rc.1 code
   * so we replicate the behaviour
   */
  return new Router(
      null,
      fakeRootComponentType,
      componentResolver,
      urlSerializer,
      routerOutletMap,
      location);
};

export const ROUTER_TEST_PROVIDERS: any[] = [
  {provide: RouterUrlSerializer, useClass: DefaultRouterUrlSerializer},
  RouterOutletMap,
  {provide: Location, useClass: SpyLocation},
  {provide: RouteSegment, useFactory: (r) => r.routeTree.root, deps: [Router]},
  {
    provide: Router,
    useFactory: routerFactory,
    deps: [FakeRootComponentLoader, ComponentResolver, RouterUrlSerializer, RouterOutletMap, Location]
  }
];

下面提供了相应的茉莉花测试。

navigation.spec.ts

import { Component } from '@angular/core';
import { beforeEach, beforeEachProviders, inject } from '@angular/core/testing';
import { ROUTER_DIRECTIVES, Route, Routes, Router } from '@angular/router';
import { TestComponentBuilder } from '@angular/compiler/testing';
import { Location } from '@angular/common';
import { ROUTER_TEST_PROVIDERS, FakeRootComponentLoader } from './router-test-providers';
/**
 * We inject router into the EmptyComponent,
 * Due to the way DI works in angular2, if we import the ROUTER_TEST_PROVIDERS, 
 * and inject the Router, we will get our own implementation of the Router injected.
 */
@Component({selector: 'empty-component', template: `empty`})
@Component({
  selector: 'empty-component',
  template: `empty`,
  directives: [ROUTER_DIRECTIVES]
})
class EmptyComponent {
    constructor (private router: Router){ }
    public getRouter() {return this.router;}
}

@Component({
  selector: 'root-component',
  template: `<router-outlet></router-outlet>`,
  directives: [ROUTER_DIRECTIVES]
})
@Routes([new Route({path: '/login', component: EmptyComponent})])
class RootComponent { }
describe('navigation', () => {
    beforeEachProviders(() => [
        {
            provide: FakeRootComponentLoader,
            useFactory: () => new FakeRootComponentLoader(RootComponent)
        },
        ROUTER_TEST_PROVIDERS,
        EmptyComponent
    ]);


    let location: Location;
    let testCb: TestComponentBuilder;
    let emptyComp: EmptyComponent;
    beforeEach(inject([Location, TestComponentBuilder, EmptyComponent], (loc, tcb, emptyCt) => {
        location = loc;
        testCb = tcb;
        emptyComp = emptyCt;

    }));

    it('should be defined', () => {
        expect(EmptyComponent).toBeDefined();
    });

    it('Should navigate to login', (done) => {
        expect(location.path()).toEqual('');
        testCb.createAsync(RootComponent).then(fixture => {
            emptyComp.getRouter().navigate(['login']).then(() => {
                fixture.detectChanges();
                expect(location.path()).toBe('/login');
                done();
            }).catch(e => done.fail(e));
        });
    });
});

答案 5 :(得分:0)

经过无数建议无效后: 这是一个在Angular4和Karma中为我工作的解决方案:

这是基于Andriy Tolstoy的回答和阅读Angular手册。

//ToBeTestedComponent.spec.ts

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ToBeTestedComponent],
      providers: [
        {
          provide: Router,
          useClass: class { navigate = jasmine.createSpy("navigate"); }
        }
      ]
    })
    .compileComponents();
  }));