Búsqueda de sitios web

reactjs - ¿Qué debería probar en este hook personalizado que envuelve a useSWR?


He creado un hook personalizado llamado useCity. Está envolviendo una llamada a la API hecha usando useSWR.

Aquí está el código para el hook:

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;

Ahora, necesito probar el hook. Así que intenté usar msw y @testing-library/react-hooks.

Aquí está mi intento:

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);
});

Asumo que el caso de prueba pasará una vez que implemente la función simulada.

Pero no sé si este es el enfoque correcto para probar un hook así. Soy desarrollador frontend, ¿es mi responsabilidad probar esa llamada a la API?

Soy nuevo en escribir casos de prueba que involucren llamadas a API. ¿Estoy yendo en la dirección correcta? No sé cómo se llaman este tipo de pruebas. Si alguien puede decirme el tipo de prueba que estoy realizando, entonces me ayudará a buscar soluciones en Google en lugar de perder el tiempo de otros desarrolladores respondiendo a mis preguntas.

Respuesta #1

Parece que estás en el camino correcto.

Tu hook useCity hace básicamente 2 cosas que puedes validar en las pruebas:

  1. construye una URL
  2. convierte las ciudades a otro formato

Puedes validar que useSWR se llame con la URL correcta usando un espía:

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

Puedes validar que useCities devuelve las ciudades correctas mediante

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

Soy un desarrollador frontend, ¿es mi responsabilidad probar esa llamada a la API?

Creo que depende de ti encontrar la respuesta. Personalmente, considero que es mi responsabilidad probar cualquier código que escriba, eso, por supuesto, no es un dogma y depende del contexto.

No sé cómo se llaman este tipo de pruebas. Si alguien puede decirme el tipo de prueba que estoy realizando, entonces me ayudará a buscar en Google las soluciones

Podría no haber una respuesta clara para esto. Algunas personas lo llamarían pruebas unitarias (ya que useCities es una "unidad"). Otros podrían llamarlo pruebas de integración (ya que pruebas useCities y useSWR en "integración").

Tu mejor opción sería buscar en Google cosas como "cómo probar react hooks" o "cómo probar componentes de react". La documentación de RTL es un buen lugar para comenzar.


Notas adicionales

Personalmente, casi nunca pruebo los hooks de forma aislada. Me resulta más fácil e intuitivo escribir pruebas de integración para los componentes que usan los hooks.

Sin embargo, si tu hook va a ser usado por otros proyectos, creo que tiene sentido probarlo de manera aislada, como lo estás haciendo aquí.

Respuesta #2

Si quieres imitar todo el SWR, puedes hacerlo; la parte desafiante es esquivar la 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();
    });
  });

Artículos relacionados