简体   繁体   English

如何在 mocking 功能时允许部分 TypeScript 类型 - 开玩笑,TypeScript

[英]How to allow partial TypeScript types when mocking functions - Jest, TypeScript

I have a function which I would like to mock for testing purposes in TypeScript.我有一个 function,我想在 TypeScript 中模拟它以进行测试。 In my tests, all I care about are the json and the status .在我的测试中,我只关心jsonstatus However, when using Jest's jest.spyOn the type of my mocked function is set to return a http Response type.但是,当使用 Jest 的 jest.spyOn 时,我模拟的jest.spyOn的类型设置为返回 http Response类型。 This is awkward as it means I have to manually go and implement a bunch of functions and properties that are irrelevant and arbitrary.这很尴尬,因为这意味着我必须手动 go 并实现一堆不相关且任意的功能和属性。

I suspect there is some way to use a partial type here to allow better and more useful mocking by overriding the return type to only that I care about.我怀疑这里有某种方法可以使用部分类型,通过将返回类型覆盖为我关心的类型,从而实现更好、更有用的 mocking。 How would I go about doing this?我将如何 go 这样做?

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
      // ...
    }),
  );
}

You can use as ...您可以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
  );
}

The as keyword, used for typecasting, when used to convert a literal to type X, will allow you to define it only partially, but you still have type-checking because you cannot define props that don't exist. as关键字,用于类型转换,当用于将文字转换为 X 类型时,将允许您仅部分定义它,但您仍然需要进行类型检查,因为您无法定义不存在的道具。

Example:例子:

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

I found a solution using the unknown type.我找到了使用unknown类型的解决方案。

After trying and failing to use as to typecast immediately, I first cast the promise to unknown and then cast this value to the desired Response type like so:在尝试并未能立即使用as进行类型转换后,我首先将 promise 转换为unknown ,然后将此值转换为所需的Response类型,如下所示:

    // ...
    .mockImplementation(() => {
      const httpResponsePromise = Promise.resolve({
        json: () => Promise.resolve(json),
        status,
      }) as unknown;
      return httpResponsePromise as Promise<Response>;
    });

I wrote the utility below which gives my codebase a partiallyMock<T>({}) call having property autocompletion for any type...我在下面编写了实用程序,它为我的代码库提供了一个partiallyMock<T>({})调用,该调用具有任何类型的属性自动完成......

在 Playground 中演示 partialMock 自动完成

在操场上演示 mockWindow 完成

/** 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM