我应该在这个包装 useSWR 的自定义钩子中测试啥?

Posted

技术标签:

【中文标题】我应该在这个包装 useSWR 的自定义钩子中测试啥?【英文标题】:What should i test in this custom hook that is wrapping useSWR?我应该在这个包装 useSWR 的自定义钩子中测试什么? 【发布时间】:2021-03-03 03:23:27 【问题描述】:

我创建了一个名为 useCity 的自定义挂钩。它正在包装使用 useSWR 进行的 API 调用。

下面是钩子的代码:

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 上搜索解决方案,而不是浪费其他开发人员的时间来回答我的问题。

【问题讨论】:

【参考方案1】:

看起来你在正确的轨道上。

您的 useCity 钩子基本上做了两件事,您可以在测试中验证:

    建立一个网址 将城市转换为另一种格式

您可以使用间谍验证 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 调用吗?

我认为这取决于您找到答案。我个人认为我有责任测试我编写的任何代码——这当然不是教条,而且是上下文相关的。

我不知道这种测试叫什么。如果有人可以告诉我我正在执行的测试类型,那么它将帮助我在谷歌上搜索解决方案

对此可能没有明确的答案。有些人会称之为单元测试(因为useCities 是一个“单元”)。其他人可能称之为集成测试(因为您在“集成”中测试 useCitiesuseSWR)。

您最好的选择是搜索“如何测试反应挂钩”或“如何测试反应组件”之类的内容。 RTL 文档是一个很好的起点。


补充说明

我个人几乎从不单独测试钩子。我发现为使用钩子的组件编写集成测试更容易、更直观。

但是,如果您的钩子将被其他项目使用,我认为单独测试它们是有意义的,就像您在这里所做的那样。

【讨论】:

感谢您的出色回答。现在我明白了我应该如何测试自定义钩子。我问了这个问题:I am a frontend developer, is this my responsibility to test that API call?,因为我认为我必须构建与 API 完全相同的功能,然后我需要对其进行测试。但我得到了我的答案。我可以测试生成的 url,然后我可以测试基本响应。无需编写函数。 另外,你能解释一下这行吗:jest.spyOn('swr', 'default');。第一个参数是什么?图书馆的名字?第二个参数是什么? 很抱歉打扰您。我在这里得到了答案:jestjs.io/docs/en/jest-object#jestspyonobject-methodname @Vishal 希望对您有所帮助!抱歉,我注意到这条线有点混乱。实际上我自己还没有测试过,但这个想法是从swr 库中监视默认导出——这样你就可以验证它是否被正确调用。我不是 100% 确定我的语法是否正确。 没问题!我将尝试编写测试用例,如果我需要更改任何内容,我会在此处发布,以便您更新您的答案,以便将来其他开发人员可以利用它。 :)

以上是关于我应该在这个包装 useSWR 的自定义钩子中测试啥?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 jest 不能为我正在测试的自定义 React 钩子提供 useTranslation 钩子?

React - nextjs 在 useEffect 中调用自定义钩子

创建永不重建的自定义钩子

如何创建一个接收依赖项的自定义钩子?

用 Jest 模拟 React 自定义钩子

vue自定义指令,包装函数节流。