【问题标题】:Angular2 RC.1 - Inject Router into unit testAngular2 RC.1 - 将路由器注入单元测试
【发布时间】:2016-05-20 22:08:13
【问题描述】:

我正在编写我的 ng2 测试,但在将路由器注入到我的组件中以进行测试时遇到了一些问题。我的组件的构造函数只接受一个参数——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();

  });



});

【问题讨论】:

    标签: angular jasmine angular2-routing


    【解决方案1】:

    您需要为路由器创建一个 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();
        });
    });
    

    【讨论】:

    • 有时候答案很简单,你看不到。 (捂脸)。这很棒!简单而有效的解决方案!谢谢!
    【解决方案2】:

    使用匿名ES 6 类和jasmine

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

    【讨论】:

    • 谢谢!在尝试了一百个不同的例子之后,这对我有用
    【解决方案3】:

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

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

    【讨论】:

    • 对于 angular2 的 beta 版本,这可以正常工作。我正在使用 RootRouter 对象不再存在的 RC.1 版本(或者如果存在,我不知道它的位置)。
    【解决方案4】:

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

    模拟路由器:

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

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

    provide(Router, {useClass: MockRouter})

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

    (仅供参考,我尝试使用ROUTER_FAKE_PROVIDERS,但仍然出现上述ComponentResolver 错误)

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

        【解决方案6】:

        经过无数次无效的建议: 这是在 Angular4 和 Karma 中对我有用的一种解决方案:

        它基于 Andriy Tolstoy 的回答并通读 Angular 手册。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-11
          • 2016-12-14
          • 1970-01-01
          • 2018-03-03
          • 1970-01-01
          • 2016-10-31
          • 1970-01-01
          • 2017-02-07
          相关资源
          最近更新 更多