jestjs - Hur man tillåter partiella TypeScript-typer när man mockar funktioner - Jest, TypeScript
Jag har en funktion som jag skulle vilja mocka för teständamål i TypeScript. I mina tester bryr jag mig bara om json och status. Men när jag använder Jest's jest.spyOn sätts typen på min mockade funktion till att returnera en http Response-typ. Detta är besvärligt eftersom det innebär att jag manuellt måste implementera en massa funktioner och egenskaper som är irrelevanta och godtyckliga.
Jag misstänker att det finns ett sätt att använda en partiell typ här för att tillåta bättre och mer användbar mocking genom att skriva över returtypen till endast det jag bryr mig om. Hur skulle jag gå tillväga för att göra detta?
export function mockApi(json: object, status: number): void {
jest.spyOn(
myApiModule,
'methodWhichReturnsAResponse'
).mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(json),
status,
// Below here is to appease jest types (not needed for
// testing purposes at the time of writing)
headers: {
has: (name: string) => true,
// get, set, etc...
},
ok: true,
redirected: false,
// and about 10 other properties which exist on the Response type
// ...
}),
);
}
Svar #1
Du kan använda as...
export function mockApi(json: object, status: number): void {
jest.spyOn(
myApiModule,
'methodWhichReturnsAResponse'
).mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(json),
status
} as http.Response), // <-- here
);
}
Nyckelordet as, som används för typomvandling, när det används för att konvertera en literal till typ X, tillåter dig att definiera det bara delvis, men du har fortfarande typkontroll eftersom du inte kan definiera egenskaper som inte finns.
Exempel :
type X {
a: number
b: number
}
const x = { a: 2 } as X // OK
const y = { a: 3, c: 2 } as X // NOT OK, because c does not exist in X
Svar #2
Jag skrev verktyget nedan som ger min kodbas ett partiallyMock<T>({})-anrop med egenskapsautokomplettering för vilken typ som helst...
/** Simple mocking inspired by https://www.npmjs.com/package/jest-mock-extended
* which has mockDeep<T>() for excellent autocompletion support but had other issues. */
/* atomic values (not made Partial when mocking) */
type Atomic = boolean | string | number | symbol | Date;
/** Mocks an indexed type (e.g. Object or Array), making it recursively Partial - note question mark */
type PartialMockIndexed<T> = {
[P in keyof T]?: PartialMock<T[P]>;
};
/** Mock any T */
export type PartialMock<T> = T extends Atomic ? T : PartialMockIndexed<T>;
/** Utility method for autocompleting a PartialMock<T> and returning it as a T */
export function partiallyMock<T>(mock: PartialMock<T>) {
return mock as T;
}
/** Window is a special object, needs special mocking */
export function mockWindow(windowMock: PartialMock<typeof window>) {
const origWindow = window;
globalThis.window = Object.create(window);
for (const [key, value] of Object.entries(windowMock)) {
Object.defineProperty(globalThis.window, key, { value });
}
const unmockWindow = (globalThis.window = origWindow);
return unmockWindow;
}

