改进此代码:使用 Jest 监视模块时如何避免使用"any"?以及如何避免通用?



我写了一段代码,它返回一个模块及其所有导出的成员作为笑话监视函数:

export const spyOnModule = <T>(mod: any) => {
const obj = {} as {
[x in keyof T]: jest.SpyInstance<any, any>
}
const modKeys = Object.keys(mod)
modKeys.forEach((x) => {
const key = x as keyof T
const currentProp = mod[key]
if (typeof currentProp === 'function') {
obj[key] = jest.spyOn(mod, key as string)
}
})
return obj
}

这就是它在测试中的使用方式:

// MyComponent.test.ts
import * as LookupApi from 'Data/LookUp/LookupApi2'
import { spyOnModule } from 'helper'
const mockLookupApi = spyOnModule<typeof LookupApi>(LookupApi)
// mockLookupApi would have all the methods as spied functions
// {
//     readonly useGetLookupsQuery: jest.SpyInstance<any, any>;
//     readonly lookupApiReducer: jest.SpyInstance<any, any>;
//     readonly lookupApiReducerPath: jest.SpyInstance<any, any>;
//     readonly lookupApiMiddleware: jest.SpyInstance<...>;
//     readonly lookupApiEndpoints: jest.SpyInstance<...>;
// }
describe('MyComponent',()=>{
it('Should call the api',()=>{
mockLookupApi.useGetLookupsQuery.mockReturnValue('abc')
// MyComponent act
expect(mockLookupApi.lookupApiReducerPath).toBeCalled()
expect(mockLookupApi.useGetLookupsQuery).toBeCalledWith(123)
})
})

非常方便,但有三点我不喜欢:

  1. 当调用spyOnModule时,我必须使用<typeof ...>来获取返回对象中的模块方法名称。我希望能够从模块中推断类型,而不必传递泛型
  2. mod是CCD_ 2类型。不太喜欢这个。模块有类型吗
  3. 我希望spied函数能够保留它们的参数和返回值。现在我只为所有这些返回<any, any>,因为我不知道如何获取参数和返回值类型

这些问题可以解决吗?

  1. 是的,只是不要指定(mod: any),否则您会用任何类型重写类型。相反,使用一个通用参数,TS将推断它,即(mod: T)

  2. 模块只是对象,所以我们可以将T约束为对象,比如:<T extends object>

  3. 是的,您必须映射到值上,然后使用条件类型来确定哪些是函数,提取参数和返回类型(下面分别用AR表示(,哪些应该保持原样。

附带说明-您当前的实现将删除导出的非函数值,这是有意的吗?

将这些结合在一起,这应该会奏效:


type WithModuleSpies<T extends object> = {
[key in keyof T]: T[key] extends (...args: infer A) => infer R
? jest.SpyInstance<R, A>
: T[key];
};
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T] &
string;
export const spyOnModule = <T extends object>(mod: T): WithModuleSpies<T> => {
const spies = { ...mod } as WithModuleSpies<T>;
for (const key in mod) {
const value = mod[key];
if (typeof value === "function") {
spies[key] = jest.spyOn(
mod,
(key as unknown) as FunctionPropertyNames<T>
);
}
}
return spies;
};
const spied = spyOnModule(MyModule);
const call = spied.foo.mock.calls[0]; // [a: number, b: number] 
const instance = spied.foo.mockImplementation((a, b) => 3); // impl has type (a: number, b: number) => number | (a: number, b: number) => undefined
const value = spied.MY_CONST; // string

相关内容

最新更新