在编写Karma-Jasmine单元测试用例时出现“Error: No provider for router”的错误

168

我们已经完成了一个Angular2项目的设置,然后使用以下命令进行了一些操作:创建了一个模块(my-module),并在该模块内创建了一个组件(my-new-component)。

ng new angular2test
cd angular2test
ng g module my-module
ng generate component my-new-component

在创建了设置和所有组件之后,我们从angular2test文件夹内的cmd运行了ng test命令。

以下是我们的 my-new-component.component.ts 文件:

import { Component, OnInit } from '@angular/core';
import { Router, Routes, RouterModule } from '@angular/router';
import { DummyService } from '../services/dummy.service';

@Component({
  selector: 'app-my-new-component',
  templateUrl: './my-new-component.component.html',
  styleUrls: ['./my-new-component.component.css']
})
export class MyNewComponentComponent implements OnInit {

  constructor(private router : Router, private dummyService:DummyService) { }

  ngOnInit() {
  }
    redirect() : void{
        //this.router.navigate(['/my-module/my-new-component-1'])
    }
}

以下文件是我们的 my-new-component.component.spec.ts 文件:

/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import { RouterTestingModule } from '@angular/router/testing';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import { DummyService } from '../services/dummy.service';

import { MyNewComponentComponent } from './my-new-component.component';

describe('MyNewComponentComponent', () => {
  let component: MyNewComponentComponent;
  let fixture: ComponentFixture<MyNewComponentComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports: [RouterTestingModule, NgbModule.forRoot(), DummyService],
      declarations: [ MyNewComponentComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(MyNewComponentComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  }); 
});
我们在运行ng test命令时遇到以下cmd错误:
    Chrome 54.0.2840 (Windows 7 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.593 secs / 2.007 secs)
Chrome 54.0.2840 (Windows 7 0.0.0) MyNewComponentComponent should create FAILED
        Failed: Unexpected value 'DummyService' imported by the module 'DynamicTestModule'
        Error: Unexpected value 'DummyService' imported by the module 'DynamicTestModule'

我们已更新组件文件和规范文件。请查看以下代码片段。

以下文件是我们的my-new-component.component.ts文件

import { Component, OnInit } from '@angular/core';
import { Router, Routes, RouterModule } from '@angular/router';
import { DummyService } from '../services/dummy.service';

@Component({
  selector: 'app-my-new-component',
  templateUrl: './my-new-component.component.html',
  styleUrls: ['./my-new-component.component.css']
})
export class MyNewComponentComponent implements OnInit {

  constructor(private router : Router, private dummyService:DummyService, public fb: FormBuilder) { 
  super(fb);
  }

  ngOnInit() {
  }
    redirect() : void{
        //this.router.navigate(['/my-module/my-new-component-1'])
    }
}

以下文件是我们的my-new-component.component.spec.ts文件:

/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { FormsModule, FormGroup, FormBuilder, Validators, ReactiveFormsModule} from '@angular/forms';
import { SplitPipe } from '../../common/pipes/string-split.pipe';
import { RouterTestingModule } from '@angular/router/testing';
import { DummyService } from '../services/dummy.service';
import { MyNewComponentComponent } from './my-new-component.component';

describe('MyNewComponentComponent', () => {
  let component: MyNewComponentComponent;
  let fixture: ComponentFixture<MyNewComponentComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports: [RouterTestingModule, DummyService ,HttpModule, FormBuilder],
      declarations: [ MyNewComponentComponent, SplitPipe]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(MyNewComponentComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  }); 
});

但是在运行ng test命令时,我们遇到了以下错误。

09 12 2016 09:13:48.987:WARN [karma]: No captured browser, open http://localhost:9876/
09 12 2016 09:13:49.008:INFO [karma]: Karma v1.2.0 server started at http://localhost:9876/
09 12 2016 09:13:49.010:INFO [launcher]: Launching browser Chrome with unlimited concurrency
09 12 2016 09:13:49.420:INFO [launcher]: Starting browser Chrome
09 12 2016 09:13:58.642:INFO [Chrome 54.0.2840 (Windows 7 0.0.0)]: Connected on socket /#QZ9LSSUVeK6KwNDlAAAA with id 46830907
        Failed: Unexpected value 'FormBuilder' imported by the module 'DynamicTestModule'
        Error: Unexpected value 'FormBuilder' imported by the module 'DynamicTestModule'
3个回答

336

在设置测试模块时,您需要导入RouterTestingModule

  /* tslint:disable:no-unused-variable */
  import { async, ComponentFixture, TestBed } from '@angular/core/testing';
  import { By } from '@angular/platform-browser';
  import { DebugElement } from '@angular/core';

  import { RouterTestingModule } from '@angular/router/testing';

  import { MyNewComponentComponent } from './my-new-component.component';

  describe('MyNewComponentComponent', () => {
    let component: MyNewComponentComponent;
    let fixture: ComponentFixture<MyNewComponentComponent>;

    beforeEach(async(() => {
      TestBed.configureTestingModule({
        imports: [RouterTestingModule],
        declarations: [ MyNewComponentComponent ]
      })
      .compileComponents();
    }));

    beforeEach(() => {
      fixture = TestBed.createComponent(MyNewComponentComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should create', () => {
      expect(component).toBeTruthy();
    });
  });

编辑:使用虚拟的 DummyService 的示例

  /* tslint:disable:no-unused-variable */
  import { async, ComponentFixture, TestBed } from '@angular/core/testing';
  import { By } from '@angular/platform-browser';
  import { DebugElement } from '@angular/core';

  import { RouterTestingModule } from '@angular/router/testing';

  import { MyNewComponentComponent } from './my-new-component.component';

  // import the service
  import { DummyService } from '../dummy.service';

  // mock the service
  class MockDummyService extends DummyService {
    // mock everything used by the component
  };

  describe('MyNewComponentComponent', () => {
    let component: MyNewComponentComponent;
    let fixture: ComponentFixture<MyNewComponentComponent>;

    beforeEach(async(() => {
      TestBed.configureTestingModule({
        imports: [RouterTestingModule],
        declarations: [MyNewComponentComponent],
        providers: [{
          provide: DummyService,
          useClass: MockDummyService
        }]
      })
        .compileComponents();
    }));

    beforeEach(() => {
      fixture = TestBed.createComponent(MyNewComponentComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('should create', () => {
      expect(component).toBeTruthy();
    });
  });

2
MockDummyService继承DummyService但没有实现,难道不会使用DummyService中的相同方法,从而使模拟无用吗? - elliotwesoff
@JayChase,你能否发布一个你创建的示例的Git链接或Stackblitz链接,这对大家都会很有帮助。 - Jay Trivedi

9
添加 RouterTestingModule 以配置 configureTestingModule 的 testCase
==> 导入: [RouterTestingModule],
import {RouterTestingModule} from '@angular/router/testing';

beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule], // <====
providers: [],
declarations: [],
});
});

在声明部分,提供您组件的名称,例如下面这样: declarations: [YourComponentName] - sid7747

7
我遇到了相同类型的错误,我想分享我的解决方案以帮助他人。
我在 Karma 中遇到的错误是:
error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'RouterModule', 'Router', 'Function', 'Function' ] })
NullInjectorError: R3InjectorError(DynamicTestModule)[RouterModule -> Router -> Function -> Function]: 
  NullInjectorError: No provider for Function!

inventory-view.component.ts

@Component({
  selector: 'app-inventory-view',
  templateUrl: './inventory-view.component.html',
  styleUrls: ['./inventory-view.component.scss'],
  animations: []
})
export class InventoryViewComponent implements OnInit, AfterViewInit, OnDestroy {

  constructor(
    public router: Router, // <--- here was the problem
    public activatedRoute: ActivatedRoute
  ) { }

In my test file

inventory-view.component.spec.ts

import { HttpClientModule } from '@angular/common/http';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ActivatedRoute, convertToParamMap, Router } from '@angular/router';

const ActivatedRouteSpy = {
  snapshot: {
    paramMap: convertToParamMap({
      some: 'some',
      else: 'else',
    })
  },
  queryParamMap: of(
    convertToParamMap({
      some: 'some',
      else: 'else',
    })
  )
};

const RouterSpy = jasmine.createSpyObj(
  'Router',
  ['navigate']
);

describe('InventoryViewComponent', () => {
  let component: InventoryViewComponent;
  let fixture: ComponentFixture<InventoryViewComponent>;

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientModule,
        RouterTestingModule,
      ],
      declarations: [ InventoryViewComponent ],
      providers: [
        { provide: ActivatedRoute,   useValue: ActivatedRouteSpy    },
        { provide: Router,           useValue: RouterSpy            }
      ]
    })
    .compileComponents();
  }));

  beforeEach(waitForAsync(() => {
    fixture = TestBed.createComponent(InventoryViewComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

2
谢谢您的建议,按照您的建议添加“{ provide: Router, useValue: RouterSpy }”解决了我的问题。 - NemraOx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接