【发布时间】:2021-08-15 14:38:15
【问题描述】:
我正在使用 ngxs 来管理我的应用的状态。
@State<EmployeesStateModel>({
name: 'employees',
defaults: {
// ...
}
})
@Injectable({
providedIn: 'root'
})
export class EmployeesState {
constructor(private employeesService: EmployeesService) {
}
@Action(GetEmployeesList)
async getEmployeesList(ctx: StateContext<EmployeesStateModel>, action: GetEmployeesList) {
const result = await this.employeesService
.getEmployeeListQuery(0, 10).toPromise();
// ...
}
}
问题
我不明白如何在我的测试中使用 jest 来模拟 EmployeesService 依赖项。与 NGXS 测试相关的文档也没有提供任何示例。
我刚刚开始测试 angular/node 应用程序,所以我不知道我在做什么。
我按照我从this SO question 那里学到的知识进行了以下测试。
describe('EmployeesStateService', () => {
let store: Store;
let employeesServiceStub = {} as EmployeesService;
beforeEach(() => {
employeesServiceStub = {
getEmployeeListQuery: jest.fn()
};
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
NgxsModule.forRoot([EmployeesState])
],
providers: [
{ provide: EmployeesService, useFactory: employeesServiceStub }
]
});
store = TestBed.inject(Store);
TestBed.inject(EmployeesService);
});
it('gets a list of employees', async () => {
employeesServiceStub = {
getEmployeeListQuery: jest.fn((skip, take) => [])
};
await store.dispatch(new GetEmployeesList()).toPromise();
const list = store.selectSnapshot(state => state.employees.employeesList);
expect(list).toStrictEqual([]);
});
});
当我尝试运行测试时,这会导致错误 TypeError: provider.useFactory.apply is not a function。
此外,我在beforeEach 函数中设置employeesServiceStub 的值时,它会抛出一个错误,指出我分配的值缺少实际EmployeesService 中的剩余属性。本质上是要求我对服务进行完整的模拟实现。这对我来说效率很低,因为在每个测试中,我都需要为不同的函数定义不同的模拟实现。
TS2740: Type '{ getEmployeeListQuery: Mock ; }' is missing the following properties from type 'EmployeesService': defaultHeaders, configuration, encoder, basePath, and 8 more.
理想情况下,在每个测试中,我应该能够在每个测试中为我的 EmployeesService 的模拟函数定义不同的返回值,而不必定义该测试不需要的函数的模拟版本。
由于EmployeesService 中的函数是异步函数,我也不知道如何为函数定义异步返回值。如果有人能对此有所了解,我将不胜感激。
最终解决方案
基于answer given by Mark Whitfield,我进行了以下更改,从而解决了我的问题。
describe('EmployeesStateService', () => {
let store: Store;
// Stub function response object that I will mutate in different tests.
let queryResponse: QueryResponseDto = {};
let employeesServiceStub = {
// Ensure that the stubbed function returns the mutatable object.
// NOTE: This function is supposed to be an async function, so
// the queryResponse object must be returned by the of() function
// which is part of rxjs. If your function is not supposed to be async
// then no need to pass it to the of() function from rxjs here.
// Thank you again Mark!
getEmployeesListQuery: jest.fn((skip, take) => of(queryResponse))
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
NgxsModule.forRoot([EmployeesState])
],
providers: [
// Correctly use the useFactory option.
{ provide: EmployeesService, useFactory: () => employeesServiceStub }
]
});
store = TestBed.inject(Store);
TestBed.inject(EmployeesService);
});
it('gets a list of employees', async () => {
// Here I mutate the response object that the stubbed service will return
queryResponse = {
// ...
};
await store.dispatch(new GetEmployeesList()).toPromise();
const list = store.selectSnapshot(state => state.employees.employeesList);
expect(list).toStrictEqual([]);
});
});
【问题讨论】:
标签: angular typescript unit-testing jestjs ngxs