编写 Karma-Jasmine 单元测试用例时出现“错误:没有路由器提供程序”

Posted

技术标签:

【中文标题】编写 Karma-Jasmine 单元测试用例时出现“错误:没有路由器提供程序”【英文标题】:"Error: No provider for router" while writing Karma-Jasmine unit test cases 【发布时间】:2017-04-22 11:51:04 【问题描述】:

我们已经完成了一个 angular2 项目设置,并在其中创建了一个模块(my-module),并在该模块中使用以下 cmd 命令创建了一个组件(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'

我们更新了组件文件和规范文件。很高兴找到下面的代码 sn-p。

以下文件是我们的 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'

【问题讨论】:

【参考方案1】:

我遇到了同样的错误,我想分享我的解决方案以帮助其他人

我在 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
  )  

在我的测试文件中

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

【讨论】:

谢谢,按照您的建议添加 provide: Router, useValue: RouterSpy 解决了我的问题。【参考方案2】:

为configureTestingModule testCase添加RouterTestingModule

==> 导入:[RouterTestingModule],

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

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

【讨论】:

在声明中,提供您的组件名称,如下所示:声明:[YourComponentName]【参考方案3】:

设置测试模块时需要导入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();
    );
  );

【讨论】:

在没有实现的情况下扩展 DummyService 的 MockDummyService 会不会利用 DummyService 中的相同方法,从而使模拟毫无意义? @JayChase 你可以发布一个 git 链接,例如你创建的或者 stackblitz 链接,它对所有人都有帮助。

以上是关于编写 Karma-Jasmine 单元测试用例时出现“错误:没有路由器提供程序”的主要内容,如果未能解决你的问题,请参考以下文章

不加载 karma-jasmine 框架中生成的测试用例

获取错误:在角度 js 中进行单元测试时出现 $injector:modulerr 模块错误

Karma-Jasmine之安装与实例详解

编写单元测试用例说明书的依据是啥

为 Preact 编写单元测试用例

如何为以下代码段编写单元测试用例