如何在玩笑单元测试中模拟私有 ngxs 状态服务依赖项/属性

H.R*_*imy 5 unit-testing typescript jestjs angular ngxs

我正在使用 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();
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

问题

我不明白如何使用 jest 来模拟EmployeesService测试中的依赖关系。与 NGXS 测试相关的文档也没有提供任何示例。

我刚刚开始测试角度/节点应用程序,所以我不知道我在做什么。

我按照从这个 SO 问题中学到的知识进行了以下测试。

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([]);
  });
});

Run Code Online (Sandbox Code Playgroud)

TypeError: provider.useFactory.apply is not a function当我尝试运行测试时,这会导致错误。

employeesServiceStub此外,当我在函数中设置 的值时beforeEach,它会抛出一个错误,指出我分配的值缺少实际EmployeesService. 本质上是要求我对服务进行完整的模拟实现。这对我来说效率非常低,因为在每个测试中,我需要为不同的函数定义不同的模拟实现。

TS2740: Type '{ getEmployeeListQuery: Mock ; }' is missing the following properties from type 'EmployeesService': defaultHeaders, configuration, encoder, basePath, and 8 more.
Run Code Online (Sandbox Code Playgroud)

理想情况下,在每个测试中,我应该能够为EmployeesService每个测试中的模拟函数定义不同的返回值,而不必定义该测试不需要的函数的模拟版本。

由于中的函数EmployeesService是异步函数,我也不知道如何为函数定义异步返回值。如果有人能阐明这一点,我将非常感激。

最终解决方案

根据Mark Whitfield 给出的答案,我进行了以下更改,从而解决了我的问题。

TS2740: Type '{ getEmployeeListQuery: Mock ; }' is missing the following properties from type 'EmployeesService': defaultHeaders, configuration, encoder, basePath, and 8 more.
Run Code Online (Sandbox Code Playgroud)

Mar*_*eld 2

您的示例中使用的提供程序定义useFactory不正确。你可以把它改成这样:

providers: [
  { provide: EmployeesService, useFactory: () => employeesServiceStub }
]
Run Code Online (Sandbox Code Playgroud)

您可以将其用于useValue您的提供程序,但这意味着您无法重新分配在 中初始化的模拟beforeEach,而必须对其进行变异:

providers: [
  { provide: EmployeesService, useValue: employeesServiceStub }
]
// then in your test...
employeesServiceStub..getEmployeeListQuery = jest.fn(....
Run Code Online (Sandbox Code Playgroud)

重新分配employeesServiceStub实际上可能仍然是您的测试的一个问题,因此您可以更改对象,或者将 TestBed 设置移到您的测试中。

注意:模拟 NGXS 状态的提供者与任何其他 Angular 服务相同。

关于您问题的第二部分,如果您说异步时指的是可观察量(我可以从您的用法中推断出),那么您可以创建一个可观察量作为结果返回。例如:

import { of } from 'rxjs';
// ...
employeesServiceStub.getEmployeeListQuery = jest.fn((skip, take) => of([]))
Run Code Online (Sandbox Code Playgroud)

附言。如果您在说异步时确实指的是承诺,那么您可以将您的方法标记为,async以便获得承诺作为结果。例如:

employeesServiceStub.getEmployeeListQuery = jest.fn(async (skip, take) => [])
Run Code Online (Sandbox Code Playgroud)