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



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

但是,当我运行测试用例时,我在尝试注入路由器时遇到错误。 我做错了什么? 谁能提供一个工作的例子?

我正在使用角度2-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();
  });

});

您需要为路由器创建一个茉莉花间谍对象。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();
    });
});

使用匿名 ES 6 类和茉莉花:

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

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

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

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

模拟路由器:

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

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

provide(Router, {useClass: MockRouter})

如果 NG2 文档能够展示如何将Router正确地注入 Jasmine 测试用例,那就太好了。 嘲笑他们的对象似乎应该是一个不必要的步骤。

(仅供参考,我尝试使用ROUTER_FAKE_PROVIDERS,但仍然收到上面的ComponentResolver错误)

这是一个替代解决方案,它有点冗长,但允许我们使用间谍位置来检查路由更改。首先,我们创建通用测试路由器提供程序。

router-test-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]
  }
];

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

导航.规格

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

经过无数不起作用的建议:这是在Angular4和Karma中对我有用的一个解决方案:

它基于安德烈·托尔斯泰的答案并通读了《角度手册》。

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

最新更新