错误:单元测试组件时没有DeepLinker的提供程序

时间:2017-01-20 22:41:01

标签: angular ionic2

ionic 2中的测试套件尚未准备就绪。所以我使用这个tutorial进行测试。这主要使用angular 2测试套件。这是我的test.ts

declare var __karma__: any;
declare var require: any;

// Prevent Karma from running prematurely.
__karma__.loaded = function (): void { /* no op */};

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context: any = require.context('./', true, /\.spec\.ts/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();

export class TestUtils {

  public static beforeEachCompiler(components: Array<any>): Promise<{fixture: any, instance: any}> {
    return TestUtils.configureIonicTestingModule(components)
    .compileComponents().then(() => {
      let fixture: any = TestBed.createComponent(components[0]);
      return {
        fixture: fixture,
        instance: fixture.debugElement.componentInstance,
      };
    });
  }

  public static configureIonicTestingModule(components: Array<any>): typeof TestBed {
    return TestBed.configureTestingModule({
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      declarations: [
      ...components
      ],
      providers: [
      NavController,LoadingController,App, Form, Keyboard, DomController, MenuController,
      { provide: Bookemon, useClass: BookMock},
      { provide: Authentication, useClass: AuthMock},
      {provide: Storage, useClass: StorageMock},
      {provide: Config, useClass: ConfigMock},
      {provide: Platform, useClass: PlatformMock},
      {provide: Events, useClass: Eventsmock}
      ],
      imports: [
      FormsModule,
      IonicModule,
      ReactiveFormsModule,
      SwingModule
      ],
    });
  }

我正在尝试测试我的tab.ts组件。

tab.ts

@Component({
  selector: 'page-tabs',
  templateUrl: 'tabs.html'
})
export class TabsPage {
  user: any;
  tab1Root: any = UserHuntsPage;
  tab2Root: any = HomePage;
  tab3Root: any = FeedPage;

  constructor(public navCtrl: NavController , public auth: Authentication ,public events: Events) {

  }
  ngOnInit(){
    this.events.subscribe('logout-user' , () => {
      this.navCtrl.setRoot(WelcomePage);
     })
  }
}

以下是tab.pec.ts

let fixture: ComponentFixture<TabsPage> = null;
let instance: any = null;
describe('Tabs component',()=>{
  beforeEach(async(() => TestUtils.beforeEachCompiler([TabsPage]).then(compiled => {
    fixture = compiled.fixture;
    instance = compiled.instance;
  })));
  it('initialises', () => {
    expect(1).toBeTruthy();
  });
})

当我运行测试时,我收到此错误,

Error: No provider for DeepLinker!
    at NoProviderError.BaseError [as constructor] (webpack:///~/@angular/core/src/facade/errors.js:24:0 <- src/test.ts:8542:34)
    at NoProviderError.AbstractProviderError [as constructor] (webpack:///~/@angular/core/src/di/reflective_errors.js:41:0 <- src/test.ts:72995:16)
    at new NoProviderError (webpack:///~/@angular/core/src/di/reflective_errors.js:72:0 <- src/test.ts:73026:16)
    at ReflectiveInjector_._throwOrNull (webpack:///~/@angular/core/src/di/reflective_injector.js:758:0 <- src/test.ts:119137:19)
    at ReflectiveInjector_._getByKeyDefault (webpack:///~/@angular/core/src/di/reflective_injector.js:786:0 <- src/test.ts:119165:25)
    at ReflectiveInjector_._getByKey (webpack:///~/@angular/core/src/di/reflective_injector.js:749:0 <- src/test.ts:119128:25)
    at ReflectiveInjector_.get (webpack:///~/@angular/core/src/di/reflective_injector.js:558:0 <- src/test.ts:118937:21)
    at TestBed.get (webpack:///~/@angular/core/bundles/core-testing.umd.js:814:0 <- src/test.ts:39277:67)
    at CompiledTemplate.proxyViewClass.AppView.injectorGet (webpack:///~/@angular/core/src/linker/view.js:109:0 <- src/test.ts:119679:45)
    at CompiledTemplate.proxyViewClass.DebugAppView.injectorGet (webpack:///~/@angular/core/src/linker/view.js:351:0 <- src/test.ts:119921:49)
Error: Uncaught (in promise): Error: Error in ./TabsPage class TabsPage - inline template:0:0 caused by: No provider for DeepLinker!
    at resolvePromise (webpack:///~/zone.js/dist/zone.js:468:0 <- src/test.ts:141307:31)
    at resolvePromise (webpack:///~/zone.js/dist/zone.js:453:0 <- src/test.ts:141292:17)
    at webpack:///~/zone.js/dist/zone.js:502:0 <- src/test.ts:141341:17
    at ZoneDelegate.invokeTask (webpack:///~/zone.js/dist/zone.js:265:0 <- src/test.ts:141104:35)
    at ProxyZoneSpec.onInvokeTask (webpack:///~/zone.js/dist/proxy.js:103:0 <- src/test.ts:110667:39)
    at ZoneDelegate.invokeTask (webpack:///~/zone.js/dist/zone.js:264:0 <- src/test.ts:141103:40)
    at Zone.runTask (webpack:///~/zone.js/dist/zone.js:154:0 <- src/test.ts:140993:47)
    at drainMicroTaskQueue (webpack:///~/zone.js/dist/zone.js:401:0 <- src/test.ts:141240:35)

我的应用中没有使用DeepLinker。我认为它可能是我的提供者中某些东西的依赖。我花了很多时间在这上面,我不明白为什么会这样。任何帮助将不胜感激。

修改

我在DeepLinker中添加了providers,但又出现了另一个错误,

Failed: Can't resolve all parameters for DeepLinker: (?, ?, ?).
Error: Can't resolve all parameters for DeepLinker: (?, ?, ?).
    at CompileMetadataResolver._getDependenciesMetadata (webpack:///~/@angular/compiler/src/metadata_resolver.js:623:0 <- src/test.ts:50686:19)
    at CompileMetadataResolver._getTypeMetadata (webpack:///~/@angular/compiler/src/metadata_resolver.js:517:0 <- src/test.ts:50580:26)
    at webpack:///~/@angular/compiler/src/metadata_resolver.js:667:0 <- src/test.ts:50730:41
    at Array.forEach (native)
    at CompileMetadataResolver._getProvidersMetadata (webpack:///~/@angular/compiler/src/metadata_resolver.js:647:0 <- src/test.ts:50710:19)
    at CompileMetadataResolver._loadNgModuleMetadata (webpack:///~/@angular/compiler/src/metadata_resolver.js:430:0 <- src/test.ts:50493:50)
    at CompileMetadataResolver.loadNgModuleMetadata (webpack:///~/@angular/compiler/src/metadata_resolver.js:313:0 <- src/test.ts:50376:29)
    at RuntimeCompiler._loadModules (webpack:///~/@angular/compiler/src/runtime_compiler.js:99:0 <- src/test.ts:69258:41)
    at RuntimeCompiler._compileModuleAndAllComponents (webpack:///~/@angular/compiler/src/runtime_compiler.js:83:0 <- src/test.ts:69242:35)
    at RuntimeCompiler.compileModuleAndAllComponentsAsync (webpack:///~/@angular/compiler/src/runtime_compiler.js:65:0 <- src/test.ts:69224:21)

修改

将提供商添加为{ provide: DeepLinker, useValue: {} }会导致新错误,

TypeError: this.parent.registerChildNav is not a function
    at new Tabs (webpack:///~/ionic-angular/components/tabs/tabs.js:173:0 <- src/test.ts:46470:25)
    at new Wrapper_Tabs (/IonicModule/Tabs/wrapper.ngfactory.js:7:18)
    at CompiledTemplate.proxyViewClass.View_TabsPage0.createInternal (/DynamicTestModule/TabsPage/component.ngfactory.js:27:20)
    at CompiledTemplate.proxyViewClass.AppView.create (webpack:///~/@angular/core/src/linker/view.js:74:0 <- src/test.ts:119644:21)
    at CompiledTemplate.proxyViewClass.DebugAppView.create (webpack:///~/@angular/core/src/linker/view.js:330:0 <- src/test.ts:119900:44)
    at CompiledTemplate.proxyViewClass.View_TabsPage_Host0.createInternal (/DynamicTestModule/TabsPage/host.ngfactory.js:16:19)
    at CompiledTemplate.proxyViewClass.AppView.createHostView (webpack:///~/@angular/core/src/linker/view.js:81:0 <- src/test.ts:119651:21)
    at CompiledTemplate.proxyViewClass.DebugAppView.createHostView (webpack:///~/@angular/core/src/linker/view.js:341:0 <- src/test.ts:119911:52)
    at ComponentFactory.create (webpack:///~/@angular/core/src/linker/component_factory.js:154:0 <- src/test.ts:54276:25)
    at initComponent (webpack:///~/@angular/core/bundles/core-testing.umd.js:852:0 <- src/test.ts:39321:53)
Error: Uncaught (in promise): Error: Error in ./TabsPage class TabsPage - inline template:0:0 caused by: this.parent.registerChildNav is not a function
    at resolvePromise (webpack:///~/zone.js/dist/zone.js:468:0 <- src/test.ts:141416:31)
    at resolvePromise (webpack:///~/zone.js/dist/zone.js:453:0 <- src/test.ts:141401:17)
    at webpack:///~/zone.js/dist/zone.js:502:0 <- src/test.ts:141450:17
    at ZoneDelegate.invokeTask (webpack:///~/zone.js/dist/zone.js:265:0 <- src/test.ts:141213:35)
    at ProxyZoneSpec.onInvokeTask (webpack:///~/zone.js/dist/proxy.js:103:0 <- src/test.ts:110667:39)
    at ZoneDelegate.invokeTask (webpack:///~/zone.js/dist/zone.js:264:0 <- src/test.ts:141212:40)
    at Zone.runTask (webpack:///~/zone.js/dist/zone.js:154:0 <- src/test.ts:141102:47)
    at drainMicroTaskQueue (webpack:///~/zone.js/dist/zone.js:401:0 <- src/test.ts:141349:35)

3 个答案:

答案 0 :(得分:4)

我使用离子标签启动器模板遇到与OP相同的问题。我设法通过结合这里的建议和单元测试教程提供的mocks.ts中的示例来完成单元测试。

首先,我通过向mocks.ts:

添加一个模拟类来模拟DeepLinker
export class DeepLinkerMock {
}

然后在test.ts中的TestBed.configureTestingModule中将其添加为提供程序:

{provide: DeepLinker, useClass: DeepLinkerMock},

您还需要为其添加导入,以及test.ts中NavMock的导入:

import {ConfigMock, PlatformMock, NavMock, DeepLinkerMock} from './mocks';

此时你的测试将失败,而this.parent.registerChildNav不是一个函数。我在ionic-angular(文件nav-controller-base.js)中查找了registerChildNav方法的签名。您需要在mocks.ts中的NavMock类中为此方法添加存根:

export class NavMock {
    ...
    public registerChildNav(nav: any): void {
        return ;
    }
}

现在你的测试将失败,没有TransitionController的提供者!添加该提供程序可以轻松解决此问题。此时我的提供者看起来像这样:

 providers: [
    App, Form, Keyboard, DomController, MenuController, GestureController, TransitionController,
    {provide: Platform, useClass: PlatformMock},
    {provide: Config, useClass: ConfigMock},
    {provide: DeepLinker, useClass: DeepLinkerMock},
    {provide: NavController, useClass: NavMock}],

注意我将NavController提供程序替换为模拟的提供程序 - 它存在于教程代码中但未被引用。 FYI GestureController也不在原始教程代码中 - 它是选项卡启动项目中其他原始四个页面之一所必需的。

不要忘记为TransitionController(和GestureController)添加导入。我的编辑(Webstorm)提出了三种选择。我选择了这个 - 不确定它是否正确:

import {TransitionController} from "ionic-angular/transitions/transition-controller";

此时,我在教程中建议的基本表单有四个单元测试,用于选项卡启动器中的四个页面。这是tabs.spec.ts:

import {Component} from "@angular/core";

import {HomePage} from "../home/home";
import {AboutPage} from "../about/about";
import {ContactPage} from "../contact/contact";

@Component({
    templateUrl: './tabs.html'
})
export class TabsPage {
    // this tells the tabs component which Pages
    // should be each tab's root Page
    tab1Root: any = HomePage;
    tab2Root: any = AboutPage;
    tab3Root: any = ContactPage;

    constructor() {

    }
}

我想我会遵循这种模式,因为我继续使用未被教程编写者嘲笑的更多框架。

答案 1 :(得分:2)

你是对的,它可能是其中一个组件的依赖关系。您是否尝试过将DeepLinker提供给TestBed,如下所示:

TestBed.configureTestingModule({
      ...
      providers: [
      DeepLinker,
      ... // the usual stuff

如果真的不需要,你也可以轻松地删除它的功能:

TestBed.configureTestingModule({
      ...
      providers: [
      { provide: DeepLinker, useValue: {} },
      ... // the usual stuff

编辑到最后一个错误:

以这种方式提供DeepLinker成功实例化并使用空对象覆盖实际的DeepLinker实现。但是,应用程序的某些部分需要缺少registerChildNav方法。您最好的选择是在覆盖原始DeepLinker实现的对象上创建此方法。

let deepLinkerStub = {
    registerChildNav: () => {}
};

TestBed.configureTestingModule({
      ...
      providers: [
      { provide: DeepLinker, useValue: deepLinkerStub },
      ... // the usual stuff

检查DeepLinker的原始实现,以查看原始registerChildNav的工作原理,并为此测试创建存根方法。

答案 2 :(得分:0)

test.ts

中更新您的提供商
 providers: [
    {provide: DeepLinker, useClass: DeepLinkerMock},
    {provide: NavController, useClass: NavMock}

    ...config.providers
  ],

以及更新mock.ts添加两个类DeepLinkerMock&amp;&amp; NavMock

export class DeepLinkerMock{
}

export class NavMock {   

  public length(): number {
    return 1;   }

  public push(): any {
    return new Promise(function(resolve: Function): void {
      resolve();
    });   }

  public getActive(): any {
    return {
      'instance': {
        'model': 'something',
      },
    };   }

  public setRoot(): any {
    return true;   }

  public pop(): any {
    return new Promise(function(resolve: Function): void {
      resolve();
    });   }

  public registerChildNav(): any {
    return new Promise(function(resolve: Function): void {
      resolve();
    });   }

  public unregisterChildNav(): any {
    return new Promise(function(resolve: Function): void {
      resolve();
    });   }

  public popToRoot(): any {
    return true;   }

  public canGoBack(): any {
    return true;   } }