我正在尝试在我的模块中测试服务,该服务依赖于另一个模块中的服务。其他模块的服务,组件......可通过npm软件包访问。像大多数人一样,我的公司对于将源代码放在网上有点挑剔,所以希望我发布的内容足以获得帮助。
我想要模拟的服务获取用户信息并存在于另一个模块中。我收到一条错误消息,指出此服务返回的属性未定义。我尝试使用TestBed.overrideModule将其他模块中的提供程序更改为我创建的模拟版本。我在查找overrideModule的文档时遇到了麻烦,我已经尝试了几天,并且无法弄清楚如何制作我想要的工作,即使在找到一些overrideModules之后也是如此。任何帮助,将不胜感激。
基于我得到的错误,我想知道是否正在注入正确的依赖关系但是不知道如何检查。错误堆栈跟踪指向webpack:///node_modules/@another-repo/prefs/index.js。
代码(如果您有任何疑问,请告诉我,我试图删除可识别我公司的事项):
以下是我的模块
// classa.service.ts
import { Injectable } from '@angular/core';
import { IDModel } from '@another-repo/core';
import { FormatService } from '@another-repo/prefs';
import { MyRepo } from './my-repo.service';
@Injectable()
export class ClassA {
constructor(idModel: IDModel, repo: MyRepo, formats: FormatService) {
}
doSomething() { }
}
// in my module
// my-repo.service.ts
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
@Injectable()
export class MyRepo {
constructor(private readonly http: Http) {
}
get(id): Observable<any> {
return this.http
.get(api(`${id}`))
.map(response => response.json());
}
}
// my-repo-service.spec.ts
//...
describe('test', () => {
let testService;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [
HttpModule,
FooModule
],
providers: [
{ provide: RepoToMock1, useClass: MockRepoToMock1 },
MyRepo,
],
});
TestBed.overrideModule(FooModule,
{
remove: {
providers: [RepoToMock1]
},
add: {
providers: [{ provide: RepoToMock1, useClass: MockRepoToMock1 }]
}
});
});
beforeEach(() => {
testService = TestBed.get(MyRepo);
});
//...
});
这是在node_modules中的index.d.ts文件中,让我们调用模块FooModule
export class IDModel {
id: Observable<number>;
constructor(otherIdRepo: OtherIdRepo, otherType: OtherTypeModel);
}
export class FormatService {
constructor(formatModel: FormatModel, timeService: TimeService, numberService, NumberService);
}
export class FormatModel {
myFormat: Format;
constructor(repo: RepoToMock1);
}
export class Format {
branding: string;
}
export class RepoToMock1 {
constructor(http: Http);
getPrefs(): Observable<Format>;
}
export class TimeService {
constructor(formatModel: FormatModel);
}
export class NumberService {
getNumber();
}
答案 0 :(得分:0)
我使用useFactory。请参阅下面的代码。
作为附注,我发现在测试中,所有依赖项都应该被模拟以进行注入。这解耦了代码库并允许更新注射剂,而不会影响依赖注射剂的物品测试。
例如,如果您要将X服务注入Y组件的测试而不模拟它,并且您决定X服务需要一个新功能,它引入了使用lodash的需要。所有组件Y的测试都会失败,因为您引入了新的依赖性并且没有考虑到它。无论您是否使用新的依赖于Lodash的功能,组件Y的测试都将失败。
如果在组件Y测试中模拟了服务X,则可以对服务X进行任何更改,而不会影响组件Y测试。
//y.component.spec.ts
describe('YComponent', () => {
let component: YComponent;
let fixture: ComponentFixture<YComponent>;
let xService: XService;
beforeEach(async(() => {
let mockXService = {
getTextColor: () => {}
};
TestBed.configureTestingModule({
declarations: [
YComponent,
],
imports: [
FormsModule
],
providers: [
{provide: XService, useFactory: () => { return mockXService; }}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents();
fixture = TestBed.createComponent(YComponent);
component = fixture.componentInstance;
roomingService = TestBed.get(XService);
fixture.detectChanges();
}));
it('should create', async(() => {
// Assert
expect(component).toBeTruthy();
}));
});
答案 1 :(得分:0)
正如@ mgm87所写,您应该模拟所有服务和组件以进行简单的单元测试。 (作为旁注,对于组件模拟,我目前正在使用ng-mocks库,因为这使模拟变得非常容易->我与该库没有任何联系)
在测试中模拟服务时,大多数时候您仍然希望能够获得有关它们的一些信息,例如是否调用它,或者返回特定值以测试代码中的不同条件。这可以通过茉莉花和间谍来简单实现。
一个组件的单元测试设置示例如下:
Subject
然后您可以简单地致电
describe(ToTestComponent.name, () => {
let component: ToTestComponent;
let fixture: ComponentFixture<ToTestComponent>;
let mockService: any;
beforeEach(async(() => {
mockService = {
coolMethod: jasmine.createSpy('coolMethod')
};
TestBed.configureTestingModule({
declarations: [
ToTestComponent,
MockComponent(SomeChildComponent) // example of ng-mocks library
],
providers: [
{provide: RealService, useValue: mockService}
]
}).compileComponents();
}));
...
});
如您所见,在这种情况下,您甚至不必导入其他模块(FooModule)。我将任何类型用作模拟服务的类型,否则您将必须满足类型要求。