TypeScript + Jest - как использовать jest.spyOn с методами generi c TypeScript? - PullRequest
0 голосов
/ 10 июля 2020

У меня определен следующий объект TypeScript:

// HttpClient.ts
export type HttpResponse<T> = Response & {
  data?: T;
}

async function get<T>(url: string, args: RequestInit = {}): Promise<HttpResponse<T>> {
  return fetchRequest<T>(url, {...args, method: 'get'}) // fetchRequest returns value of type Promise<HttpResponse<T>>
}

export default {get} // default export represents an HttpClient object

У меня также есть другой файл getFeatureFlag.ts, который использует этот HttpClient в своей реализации:

// getFeatureFlag.ts
import HttpClient from '../httpClient'

export type FetchResponse<T = any, E = any> = {
  data: Nullable<T>;
  error: Nullable<E>;
}

export type FeatureFlag = {
  toggleActive: boolean; // true/false denotes show/hide feature
}

export async function getFeatureFlag(flagName: string): Promise<FetchResponse<boolean, string>> {
  const response = await HttpClient.get<FeatureFlag>(`/v1/toggles/${flagName}/`)

  // ... rest of the implementation ...
}

I Я в процессе написания модульных тестов (с использованием Jest) для этого файла под getFeatureFlag.test.ts, и у меня есть следующий фрагмент в нем:

// getFeatureFlag.test.ts
import HttpClient, {HttpResponse} from '../httpClient'
import {getFeatureFlag, FLAG_STORE, FeatureFlag} from '../index'

describe('getFeatureFlag', () => {
  beforeEach(async () => {
    // vv === THIS LINE IS WHAT THIS POST IS ABOUT === vv
    jest.spyOn<HttpResponse<FeatureFlag>, 'get'>(HttpClient, 'get').mockImplementationOnce(() => ({
      ok: true,
      data: {
        toggleActive: true,
      },
    }))

    result = await getFeatureFlag(flagName)
  })

  it('then perform api request for feature flag data', () => {
    expect(HttpClient.get).toHaveBeenCalledWith('/v1/toggles/featureFlag/')
  })
})

Из файла определений @types/jest, spyOn функция определяется следующим образом:

function spyOn<T extends {}, M extends FunctionPropertyNames<Required<T>>>(
        object: T,
        method: M
    ): Required<T>[M] extends (...args: any[]) => any
        ? SpyInstance<ReturnType<Required<T>[M]>, ArgsType<Required<T>[M]>>
        : never;

Мне очень трудно заставить компилятор TypeScript правильно читать приведенный ниже фрагмент без проблем.

jest.spyOn<HttpResponse<FeatureFlag>, 'get'>(HttpClient, 'get').mockImplementationOnce(() => ({
      ok: true,
      data: {
        toggleActive: true,
      },
    }))

С указанной выше настройкой , компилятор сообщает мне, что get не удовлетворяет ограничению never, и я не понимаю, как решить здесь проблему ввода, чтобы мой тест был написан / запущен правильно. Другая вещь, которую я здесь пытаюсь сделать, - это вернуть только Partial из HttpResponse, потому что я, очевидно, не хочу заполнять все поля в моем модульном тесте, когда они не нужны.

Кто-нибудь смог правильно применить jest.spyOn с дженериками, чтобы решить эту проблему?

1 Ответ

0 голосов
/ 10 июля 2020

Я думаю, все, что вам не хватает, - это слово async в вашем звонке на mockImplementationOnce(). Затем вы можете удалить параметры типа для jest.spyOn. Вы не получаете автоматическую безопасность типов в своей макетной реализации, но если вас это действительно беспокоит, вы можете добавить as HttpResponse<FeatureFlag>.

let result: FetchResponse<boolean, string>;

describe('getFeatureFlag', () => {
    beforeEach(async () => {
        //                                                  missing
        //                                                     ↓
        jest.spyOn(HttpClient, 'get').mockImplementationOnce(async () => ({
            ok: true,
            data: {
                toggleActive: true,
            },
        } as HttpResponse<FeatureFlag>));

        result = await getFeatureFlag(flagName);
    })
});

TS Playground

Конечно, это не отвечает на вопрос, заданный в заголовке, но должно исправить ваш код. Я долго возился, пытаясь заставить ваши указанные аргументы типа generi c работать, и в конце концов сдался. Это может быть ограничением объявлений в пространстве имен jest.

...