使用Karma运行离子测试的StaticInjectionError

时间:2018-10-10 11:06:22

标签: angular ionic-framework ionic3 karma-jasmine

我正在尝试运行一个简单的测试来检查是否已创建组件。

使用业力karma start ./test-config/karma.conf.js运行测试时,得到以下输出:

10 10 2018 10:50:22.316:ERROR [HeadlessChrome 0.0.0 (Linux 0.0.0) | Create account component | should be created]: Error: StaticInjectorError(t)[t -> t]: 
  StaticInjectorError(Platform: core)[t -> t]: 
    NullInjectorError: No provider for t!
error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 't', Function ], ngDebugContext: t({ view: Object({ def: Object({ factory: Function, nodeFlags: 33603585, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 49152, directChildFlags: 49152, childMatchedQueries: 0, matchedQueries: Object({  }), matchedQueryIds: 0, references: Object({  }), ngContentIndex: null, childCount: 1, bindings: [  ], bindingFlags: 0, outputs: [  ], element: Object({ ns: '', name: 'page-create-account', attrs: [  ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 49152, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0,  ...
    at t.get (http://localhost:9876/base/test-config/karma-test-shim.js?6323aa11dcffe596b12ee89cf9d5010482d220c9:45:142)

这是要测试的组件(我简化为隔离问题的最简单逻辑):

import {Component} from '@angular/core';
import {NavController} from 'ionic-angular';
import {FormBuilder, FormGroup} from "@angular/forms";
import {UsernameValidator} from "../../validators/username";

@Component({
    selector: 'page-create-account',
    templateUrl: 'create-account.html',
})
export class CreateAccountPage {
    userForm: FormGroup;
    username;
    email;
    password;

    constructor(public navCtrl: NavController,
        public formBuilder: FormBuilder,
        public usernameValidator: UsernameValidator){
        this.userForm = this.formBuilder.group({});
    }
}

这是测试:

import {async, TestBed} from '@angular/core/testing';
import {IonicModule, Nav, NavController} from 'ionic-angular';
import {
  UsernameValidatorMock,
  FormBuilderMock,
  NavControllerMock,
  NavMock
} from '../../../test-config/mocks-ionic';
import {CreateAccountPage} from "./create-account";
import {FormBuilder} from "@angular/forms";
import {UsernameValidator} from "../../validators/username";
import {UserService} from "../../services/user.service";
import {Observable} from "rxjs";

describe('Create account component', () => {
  let fixture;
  let component;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [CreateAccountPage],
      imports: [
        IonicModule.forRoot(CreateAccountPage)
      ],
      providers: [
        { provide: Nav, useClass: NavMock},
        { provide: NavController, useClass: NavControllerMock},
        { provide: FormBuilder, useClass: FormBuilderMock},
        { provide: UserService, useClass: UserServiceMock},
        { provide: UsernameValidator, useClass: UsernameValidatorMock },
      ]
    })
  }));

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

  it('should be created', () => {
    expect(component instanceof CreateAccountPage).toBe(true);
  });

});

这是原始的UsernameValidator:

import { Injectable } from '@angular/core';
import { FormControl } from '@angular/forms';
import {UserService} from '../services/user.service';

@Injectable()
export class UsernameValidator {

  debouncer: any;
  object_username: any = {};
  constructor(public userService: UserService) {

  }
  checkUsername(control: FormControl): Promise<any> {

    clearTimeout(this.debouncer);

    return new Promise(resolve => {
      this.debouncer = setTimeout(() => {
        this.object_username = {username: control.value};
        this.userService.validateUsername(this.object_username).subscribe((res) => {
          console.log(res);
          if ( res === true) {
            resolve({'username taken': true});
          }
        }, (err) => {
          resolve(null);
        });
      }, 1000);

    });
  }
}

似乎无法注入某些提供程序,但是我已经为所有使用的依赖项甚至第二级依赖项(例如UserProvider for UsernameValidator)创建了模拟程序。测试成功通过,没有注入UsernameValidator。知道为什么不能注入此类吗?

版本:

Ionic:

   ionic (Ionic CLI)  : 4.1.1
   Ionic Framework    : ionic-angular 3.9.2
   @ionic/app-scripts : 3.2.0

Cordova:

   cordova (Cordova CLI) : 8.0.0
   Cordova Platforms     : android 6.4.0, ios 4.5.5

2 个答案:

答案 0 :(得分:0)

我在没有自定义类的情况下运行了您的代码,并且效果很好。 过去,我在使用离子角3.9.2时遇到了Ionic cli 4的问题。

再次使用ionic-cli 3.20.0进行检查:

[13:23:07]> (dev)$ionic info

cli packages: (E:\Usuarios\dceresa\AppData\Roaming\npm\node_modules)

    @ionic/cli-utils  : 1.19.2
    ionic (Ionic CLI) : 3.20.0

global packages:

    cordova (Cordova CLI) : 8.0.0

local packages:

    @ionic/app-scripts : 3.1.5
    Cordova Platforms  : android 6.2.3 browser 5.0.3
    Ionic Framework    : ionic-angular 3.9.2-201807062122

以防万一您对我的.spec感到好奇:

import { async, TestBed } from '@angular/core/testing';
import { IonicModule, Nav, NavController } from 'ionic-angular';
import { CreateAccountPage } from "./create-account";
import { FormBuilder } from "@angular/forms";
import { Observable } from "rxjs";
import { NavControllerMock } from 'ionic-mocks';

fdescribe('Create account component', () => {
    let fixture;
    let component;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [CreateAccountPage],
            imports: [
                IonicModule.forRoot(CreateAccountPage)
            ],
            providers: [
                { provide: NavController, useClass: NavControllerMock },
                FormBuilder
            ]
        })
    }));

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

    it('should be created', () => {
        expect(component instanceof CreateAccountPage).toBe(true);
    });

});

以及组件:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { FormBuilder, FormGroup } from "@angular/forms";

@Component({
  selector: 'page-create-account',
  templateUrl: 'create-account.html',
})
export class CreateAccountPage {
  userForm: FormGroup;
  username;
  email;
  password;

  constructor(public navCtrl: NavController,
    public formBuilder: FormBuilder) {
    this.userForm = this.formBuilder.group({});
  }
}

答案 1 :(得分:0)

我终于发现了问题。 UserService取决于HttpClient,该提供程序未包含该模拟。这是必要的,因为UsernameValidatorMockUsernameValidator的子类,因此必须注入原始类的所有依赖项。

我可以获得具体错误:

StaticInjectorError(Platform: core)[UserService -> HttpClient]: 
        NullInjectorError: No provider for HttpClient!

代替:

  StaticInjectorError(Platform: core)[t -> t]: 
    NullInjectorError: No provider for t!

通过删除webpack的4.2.0版本(因为业力已经取决于webpack)并将ts-loader从5.4.1降级到3.4.0。

尽管如此,我最终还是使用了一个验证器类的间谍,如下所示:

 beforeEach(async(() => {

        userValidatorUsernameSpy = jasmine.createSpyObj('UsernameValidator', ['checkUsername']);
        TestBed.configureTestingModule({
          declarations: [CreateAccountPage],
          imports: [
            FormsModule,
            ReactiveFormsModule,
            IonicModule.forRoot(CreateAccountPage)
          ],
          providers: [
            {provide: NavController, useValue: navControllerSpy},
            {provide: NavParams, useFactory: () => NavParamsMock.instance()},
            {provide: Platform, useFactory: () => PlatformMock.instance()},
            {provide: UsernameValidator, useValue: usernameSpy},
          ]
        }).compileComponents();

    fixture = TestBed.createComponent(CreateAccountPage);
    component = fixture.componentInstance;
  }
  ));