简体   繁体   English

模拟Angular2应用程序单元测试服务时出错

[英]Error during mocking the service for Angular2 application unit test

I've created a component which I'm trying to test using Karma and Jasmine. 我创建了一个组件,尝试使用Karma和Jasmine进行测试。 Everything works fine for other components that doesn't have services injected by DI. 对于没有通过DI注入服务的其他组件,一切正常。 But this one throws an error without any message and just with a stack. 但这会引发错误,没有任何消息,只有堆栈。

Here's the component: 这是组件:

import {Component} from 'angular2/core';

import {Application} from './application';
import {ApplicationsService} from './applications.service';

@Component({
    selector: 'applications-selector',
    styles: [require('./applications-selector.scss')],
    template: require('./applications-selector.html'),
    providers: [ApplicationsService]
})

export class ApplicationsSelectorComponent {
    applications: Application[];
    selectedWeek: number;
    selectedApplications: Application[];
    selectedCycle: string;

    constructor(private _applicationsService: ApplicationsService) {
        this.getApplications();
    }

    getApplications() {
        this._applicationsService.getApplications().then(applications => this.applications = applications);
    }
}

And here's the unit test for this component: 这是此组件的单元测试:

import {
  it,
  inject,
  injectAsync,
  describe,
  beforeEachProviders,
  TestComponentBuilder
} from 'angular2/testing';
import {provide} from 'angular2/core';

import {ApplicationsSelectorComponent} from './applications-selector.component';
import {ApplicationsService} from './applications.service';

class ApplicationsServiceMock {
  getApplications() {
      return ['ABC', 'XYZ'];
  }
}

describe('ApplicationsSelectorComponent', () => {
    beforeEachProviders(() => [
        provide(ApplicationsService, { useClass: ApplicationsServiceMock }),
        ApplicationsSelectorComponent
    ]);

    it('should have empty default values', inject([ApplicationsSelectorComponent], (component) => {
        expect(component.selectedWeek).toBe(undefined);
        expect(component.selectedApplications).toBe(undefined);
        expect(component.selectedCycle).toBe(undefined);
    }));
}); 

And here's an error that I get as soon as I run this test: 运行此测试后,我立即得到一个错误:

ApplicationsSelectorComponent
    × should have empty default values
      PhantomJS 2.1.1 (Windows 7 0.0.0)
    _instantiateProvider@d:/git/gatekeeper/web/spec-bundle.js:11896:38 <- webpack:///angular2/src/core/di/injector.ts:770:31
    _new@d:/git/gatekeeper/web/spec-bundle.js:11885:42 <- webpack:///angular2/src/core/di/injector.ts:759:37
    getObjByKeyId@d:/git/gatekeeper/web/spec-bundle.js:11495:55 <- webpack:///angular2/src/core/di/injector.ts:356:44
    _getByKeyDefault@d:/git/gatekeeper/web/spec-bundle.js:12083:51 <- webpack:///angular2/src/core/di/injector.ts:977:44
    _getByKey@d:/git/gatekeeper/web/spec-bundle.js:12029:42 <- webpack:///angular2/src/core/di/injector.ts:914:35
    get@d:/git/gatekeeper/web/spec-bundle.js:11704:31 <- webpack:///angular2/src/core/di/injector.ts:577:26
    d:/git/gatekeeper/web/spec-bundle.js:9128:74 <- webpack:///angular2/src/testing/test_injector.ts:151:52
    map@[native code]
    apply@[native code]
    call@[native code]
    call@[native code]
    map@d:/git/gatekeeper/web/spec-bundle.js:2377:21 <- webpack:///~/es6-shim/es6-shim.js:1113:0
    execute@d:/git/gatekeeper/web/spec-bundle.js:9128:39 <- webpack:///angular2/src/testing/test_injector.ts:151:34
    execute@d:/git/gatekeeper/web/spec-bundle.js:9017:27 <- webpack:///angular2/src/testing/test_injector.ts:42:22
    d:/git/gatekeeper/web/spec-bundle.js:8393:58 <- webpack:///angular2/src/testing/testing.ts:137:49
    _instantiate@d:/git/gatekeeper/web/spec-bundle.js:12003:87 <- webpack:///angular2/src/core/di/injector.ts:883:67

An error occurs on inject([ApplicationsSelectorComponent] statement. As soon as I remove it, there's no error, but I need this component to perform tests on it. inject([ApplicationsSelectorComponent]语句上发生错误。删除它后就没有错误,但是我需要此组件对其执行测试。

What can cause this injection error? 什么会导致这种注入错误?

It seems like you're trying to inject components the same way as providers which will not work. 似乎您正在尝试以与提供程序不一样的方式注入组件。

Here is complete minimal example of mocking providers for specific component: 这是针对特定组件的模拟提供程序的完整的最小示例:

class ApplicationsService {
  getApplications() {
    return ['ABC'];
  }
}

class ApplicationsServiceMock {
  getApplications() {
    return ['ABC', 'XYZ'];
  }
}

@Component({
  selector: 'apps',
  template: '',
  providers: [ApplicationsService]
})
class ApplicationsSelectorComponent {
  constructor(private apps: ApplicationsService) {}
}

describe('App', () => {

  describe('ApplicationsSelectorComponent', () => {
    beforeEach(injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
      return tcb
        .overrideProviders(ApplicationsSelectorComponent, [provide(ApplicationsService, { useClass: ApplicationsServiceMock })])
        .createAsync(ApplicationsSelectorComponent)
        .then((componentFixture: any) => {
          this.component = componentFixture;
        });
    }));

    it('should have empty default values', () => {
      expect(this.component.componentInstance.apps.getApplications()).toEqual(['ABC', 'XYZ'])
    });

  });

});

Finally, it turned out that all the setup was correct but I was just returning incorrect value from ApplicationsServiceMock . 最后,事实证明所有设置都是正确的,但是我只是从ApplicationsServiceMock返回不正确的值。 Base service is returning Promise and I was returnig just an array of value in my mock. 基本服务返回Promise而我只是在我的模拟中返回一个值数组。 That's why when this line this._applicationsService.getApplications().then(applications => this.applications = applications); 这就是为什么当此行this._applicationsService.getApplications().then(applications => this.applications = applications); was executed from constructor no then method on array could be found. 是从constructor执行的, 找不到数组上的方法。 And tests were failing. 测试失败了。
As soon as I've fixed return value from my mock everything works fine. 一旦我从模拟中确定了返回值,一切就可以正常工作。
Here's working code for my test: 这是我测试的工作代码:

import {
    it,
    beforeEach,
    injectAsync,
    describe,
    TestComponentBuilder
} from 'angular2/testing';
import {Component, provide} from 'angular2/core';

import {Application} from './application';
import {ApplicationsService} from './applications.service';
import {ApplicationsSelectorComponent} from './applications-selector.component';


class ApplicationsServiceMock {
    getApplications() {
        return Promise.resolve([{ 'id': 1, 'name': 'TST', 'project': 'PRJ' }]);
    }
}


describe('ApplicationsSelectorComponent', () => {
  beforeEach(injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
    return tcb
      .overrideProviders(ApplicationsSelectorComponent, [provide(ApplicationsService, { useClass: ApplicationsServiceMock })])
      .createAsync(ApplicationsSelectorComponent)
      .then((componentFixture: any) => {
        this.component = componentFixture;
      });
  }));

  it('should have empty default values', () => {
      expect(this.component.componentInstance._applicationsService.getApplications().then(apps => { apps.toEqual([{ 'id': 1, 'name': 'TST', 'project': 'PRJ' }]) }));
  });

});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM