为 Angular2 应用程序单元测试模拟服务时出错
Error during mocking the service for Angular2 application unit test
我创建了一个组件,我正在尝试使用 Karma 和 Jasmine 对其进行测试。对于没有 DI 注入服务的其他组件,一切正常。但是这个抛出一个错误,没有任何消息,只有一个堆栈。
组件如下:
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);
}
}
下面是这个组件的单元测试:
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);
}));
});
这是我在 运行 这个测试中得到的一个错误:
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
inject([ApplicationsSelectorComponent]语句出现错误,我一去掉就没有错误了,但是我需要这个组件来进行测试。
什么会导致此注入错误?
您似乎正试图以与提供程序相同的方式注入组件,但该方法不起作用。
这是针对特定组件模拟提供程序的完整最小示例:
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'])
});
});
});
最后,事实证明所有设置都是正确的,但我只是 return 从 ApplicationsServiceMock
中输入了错误的值。基本服务是 returning Promise
而我 returnig 只是我模拟中的一个值数组。这就是为什么当从 constructor
执行这一行 this._applicationsService.getApplications().then(applications => this.applications = applications);
时,找不到数组上的 then 方法。测试失败了。
一旦我从模拟中修复了 return 值,一切正常。
这是我测试的工作代码:
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' }]) }));
});
});
我创建了一个组件,我正在尝试使用 Karma 和 Jasmine 对其进行测试。对于没有 DI 注入服务的其他组件,一切正常。但是这个抛出一个错误,没有任何消息,只有一个堆栈。
组件如下:
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);
}
}
下面是这个组件的单元测试:
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);
}));
});
这是我在 运行 这个测试中得到的一个错误:
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
inject([ApplicationsSelectorComponent]语句出现错误,我一去掉就没有错误了,但是我需要这个组件来进行测试。
什么会导致此注入错误?
您似乎正试图以与提供程序相同的方式注入组件,但该方法不起作用。
这是针对特定组件模拟提供程序的完整最小示例:
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'])
});
});
});
最后,事实证明所有设置都是正确的,但我只是 return 从 ApplicationsServiceMock
中输入了错误的值。基本服务是 returning Promise
而我 returnig 只是我模拟中的一个值数组。这就是为什么当从 constructor
执行这一行 this._applicationsService.getApplications().then(applications => this.applications = applications);
时,找不到数组上的 then 方法。测试失败了。
一旦我从模拟中修复了 return 值,一切正常。
这是我测试的工作代码:
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' }]) }));
});
});