【问题标题】:"Error: No provider for router" while writing Karma-Jasmine unit test cases编写 Karma-Jasmine 单元测试用例时出现“错误:没有路由器提供程序”
【发布时间】: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'

【问题讨论】:

    标签: unit-testing angular karma-jasmine angular2-routing angular-cli


    【解决方案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 链接,它对所有人都有帮助。
    猜你喜欢
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多