Angular 无法在测试运行期间解析参数

Angular can't resolve parameters during test runs

我正在使用 Angular 5.2.5 开发一个 Ionic 项目,尝试使用 Jasmine 和 Karma 构建一个测试设置(面向 this example,虽然是较旧的版本)。

对于已经存在且(在手动测试中)正常工作的组件 LoginComponent,我创建了以下测试:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthenticationProvider } from '../../providers/authentication/authentication';
import { IonicModule, ToastController, ViewController } from 'ionic-angular';
import { LoginComponent } from './login';

describe('LoginComponent', () => {

  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        LoginComponent
      ],
      imports: [
        IonicModule.forRoot(LoginComponent),
      ],
      providers: [
        { provide: AuthenticationProvider, useValue: {} as AuthenticationProvider },
        { provide: ViewController, useValue: {} as ViewController },
        { provide: ToastController, useValue: {} as ToastController },
      ]
    })
  }));

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

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

要测试的组件:

import { Credentials } from '../../model/credentials';
import { AuthenticationProvider } from '../../providers/authentication/authentication';
import { ViewController, ToastController } from 'ionic-angular';

@Component({
  selector: 'login',
  templateUrl: 'login.html'
})
export class LoginComponent {

  public registration = false;
  public readonly credentials = new Credentials('', '');

  constructor(
    private authentication: AuthenticationProvider,
    private view: ViewController,
    private toast: ToastController,
  ) {
  }

  // (methods omitted for brevity)
}

启动Karma后,编译成功并触发测试。然后,它在运行时失败并显示以下错误消息:

Error: Can't resolve all parameters for LoginComponent: (?, ?, ?).
    at syntaxError (webpack:///node_modules/@angular/compiler/esm5/compiler.js:485:21 <- test-config/karma-test-shim.js:72827:34)
    at CompileMetadataResolver._getDependenciesMetadata (webpack:///node_modules/@angular/compiler/esm5/compiler.js:15700:0 <- test-config/karma-test-shim.js:88042:35)
    at CompileMetadataResolver._getTypeMetadata (webpack:///node_modules/@angular/compiler/esm5/compiler.js:15535:0 <- test-config/karma-test-shim.js:87877:26)
    at CompileMetadataResolver.getNonNormalizedDirectiveMetadata (webpack:///node_modules/@angular/compiler/esm5/compiler.js:15020:0 <- test-config/karma-test-shim.js:87362:24)
    at CompileMetadataResolver._getEntryComponentMetadata (webpack:///node_modules/@angular/compiler/esm5/compiler.js:15848:25 <- test-config/karma-test-shim.js:88190:45)
    at webpack:///node_modules/@angular/compiler/esm5/compiler.js:15829:29 <- test-config/karma-test-shim.js:88171:48
[...]

对我来说,这听起来像是构造函数参数的依赖注入失败了。但是我已经在我的测试模块配置中列出了所有这些类型的相应提供程序,所以如果我没记错的话应该使用这些提供程序。

我错过了什么?

有什么方法可以让 Angular 报告有关错误的更多详细信息,例如具体哪些参数无法解析?


更新

进一步回火发现,连每一个参数都单独查不到。也就是说,即使构造函数只是

constructor(private toast: ToastController) { }

出现同样的错误(但只有一个未解析的参数有一个问号)。由于 ToastController 是 ionic-angular 内置的,循环依赖问题可能会被排除。


更新 2

调试发现问题不在注册提供者这边,而是一开始就无法确定参数类型

在 JIT 编译器的某个时刻,以下代码片段生成 [undefined],对应于上面提到的一个参数 (toast):

this._reflector.parameters(typeOrFunc)

看起来类型信息在编译过程中以某种方式丢失了。

你能尝试做这样的事情吗?如果有效,请在评论中写下?

class AuthenticationProviderMock extends AuthenticationProvider {}
class ViewControllerMock extends ViewController {}
class ToastControllerMock extends ToastController {}

providers: [
        { provide: AuthenticationProvider, useClass: AuthenticationProviderMock },
        { provide: ViewController, useClass: ViewControllerMock },
        { provide: ToastController, useClass: ToastControllerMock },
      ]

我终于解决了这个问题。

我的 karma-test-shim.js 中有一个(未使用的)导入(加载 angular 测试代码和初始化测试环境的助手):

import { AppModule } from '../src/app/app.module';

删除它终于让我的测试变绿了。

坦率地说,我不明白为什么这会破坏依赖注入,或者它与错误消息和我的调试结果有何关系。