Что я должен протестировать в этом настраиваемом хуке, который обертывает useSWR?

Я создал специальный хук под названием useCity. Он завершает вызов API, сделанный с использованием useSWR.

Вот код для хука:

import useSWR from 'swr';

import { City } from '../interfaces';
import { BASE_URL } from '../../config';

interface CitiesResponse {
  data?: {
    records: {
      fields: {
        city: string;
        accentcity: string;
      }
    }[]
  },
  error?: {
    message: string;
  }
};

interface Props {
  start?: number;
  rows: number;
  query?: string;
  sort?: 'population';
  exclude?: string[];
}

const useCity = ({ start = 0, rows, query, sort, exclude }: Props) => {
  const params = [`start=${start}`, `rows=${rows}`];
  if (query) params.push(`q=${query}`);
  if (sort) params.push(`sort=${sort}`);
  if (exclude && exclude.length > 0) params.push(...exclude.map(city => `exclude.city=${city}`))

  const { data, error }: CitiesResponse = useSWR(
    `${BASE_URL.CITIES_SERVICE}?dataset=worldcitiespop&facet=city&${params.join('&')}`,
    { revalidateOnFocus: false,  }
  );

  const cities: City[] = data?.records.map(record => ({
    name: record.fields.city,
    title: record.fields.accentcity,
  })) || [];

  return {
    cities,
    loading: !error && !data,
    error,
  };
};

export default useCity;

Теперь мне нужно проверить крючок. Итак, я попытался использовать msw и @testing-library/react-hooks.

Вот моя попытка:

const server = setupServer(
  rest.get(BASE_URL.CITIES_SERVICE, (req, res, ctx) => {
    const start = req.url.searchParams.get('start');
    const rows = req.url.searchParams.get('rows');
    const query = req.url.searchParams.get('query');
    const sort = req.url.searchParams.get('sort');
    const exclude = req.url.searchParams.getAll('exclude.city');

    const getReturnVal: () => DatabaseCity[] = () => {
      // i will write some code that assumes what server will return
    };


    return res(
      ctx.status(200),
      ctx.json({
        records: getReturnVal(),
      }),
    );
  }),
  ...fallbackHandlers,
);

beforeAll(() => server.listen());
afterEach(() => {
  server.resetHandlers();
  cache.clear();
});
afterAll(() => server.close());


it('should return number of cities equal to passed in rows', async () => {
  const wrapper = ({ children } : { children: ReactNode }) => (
    <SWRConfig value={{ dedupingInterval: 0 }}>
      {children}
    </SWRConfig>
  );

  const { result, waitForNextUpdate, } = renderHook(() => useCity({ rows: 2 }), { wrapper });
  const { cities:_cities, loading:_loading, error:_error } = result.current;
  expect(_cities).toHaveLength(0);
  
  await waitForNextUpdate();
  
  const { cities, loading, error } = result.current;
  expect(cities).toHaveLength(2);
});

Я полагаю, что тестовый пример пройдет, как только я реализую фиктивную функцию.

Но я не знаю, правильный ли это подход к тестированию такого крючка. Я фронтенд-разработчик, обязан ли я тестировать этот вызов API?

Я новичок в написании тестовых примеров, включающих вызовы API. Я иду в правильном направлении? Я не знаю, как называются такого рода тесты. Если кто-то может сказать мне, какой тест я выполняю, это поможет мне найти решения в Google, а не тратить время других разработчиков на ответы на мои вопросы.

2 ответа

Решение

Похоже, вы на правильном пути.

Твой useCity hook выполняет в основном 2 вещи, которые вы можете проверить в тестах:

  1. строит URL
  2. конвертирует города в другой формат

Вы можете проверить useSWR вызывается с правильным URL-адресом с помощью шпиона:

import * as SWR from 'swr';

jest.spyOn(SWR, 'default'); // write this line before rendering the hook.
expect(SWR.default).toHaveBeenCalledWith(expectedUrl, {}); // pass any options that were passed in actual object

Вы можете проверить useCities возвращает правильные города по

const { cities } = result.current;
expect(cities).toEqual(expectedCities);

Я фронтенд-разработчик, обязан ли я тестировать этот вызов API?

Я думаю, что вам решать. Я лично считаю своей обязанностью тестировать любой код, который я пишу - это, конечно, не догма и зависит от контекста.

Я не знаю, как называются такого рода тесты. Если кто-то может сказать мне, какой тест я выполняю, это поможет мне найти решения в Google.

На это не может быть однозначного ответа. Некоторые люди назвали бы это модульным тестированием (поскольку useCitiesэто "единица"). Другие могут назвать это интеграционным тестированием (поскольку вы тестируете useCities и useSWR в "интеграции").

Лучше всего будет погуглить такие вещи, как "как тестировать перехватчики реакции" или "как тестировать компоненты реакции". Документация RTL - хорошее место для начала.


Дополнительные примечания

Лично я почти никогда не тестирую хуки изолированно. Я считаю, что проще и интуитивнее писать интеграционные тесты для компонентов, использующих хуки.

Однако, если ваш хук будет использоваться в других проектах, я думаю, имеет смысл тестировать их изолированно, как вы это делаете здесь.

Если вы хотите высмеять весь SWR, вы можете это сделать, задача состоит в том, чтобы уклониться от SWRResponse.

      
import { render, screen, waitFor } from '@testing-library/react';
import * as SWR from 'swr';


...

  it('should render profile', async () => {
    jest
      .spyOn(SWR, 'default')
      .mockImplementation(() => ({ data: identifyProfile, isValidating: false, mutate: () => Promise.resolve() }));

    render(<MyProfileSection />);

    await waitFor(() => {
      expect(screen.getByText('John Duo')).toBeInTheDocument();
    });
  });
Другие вопросы по тегам