likes
comments
collection
share

react-query手把手教程22-单元测试

作者站长头像
站长
· 阅读数 33

场景

在日常的开发过程中,测试对于代码质量的保证是非常重要的。一般团队中都会配备一名或几名测试工程师,手动测试当前的代码中是否正常。但是一旦应用越来越复杂的时候,此时自动化测试的重要性就会提上日程。

本文主要介绍的是如何在自动化测试脚本中使用react-query。

在测试过程中需要编写与用户操作相同的测试。因此其中应该包括实际的点击按钮,输入字段以及api请求等。接下来首先将会从api请求开始入手。

实践

api mock数据

有以下几种办法可以作为单元测试时,api请求的解决方案:

  • 为测试环境创建单独的数据库和后端,但是如果几个用户同时访问该数据库,可能会由于数据冲突最后测试失败。
  • 让每个开发人员创建自己的数据库,但仍然会遇到延迟问题。这些发出的请求并不是实时的,而且大量的数据库调用可能会导致测试需要很长时间才能完成。
  • 使用mock api来模拟调用。由于mock api与真实api几乎相同,并且无需与数据库通信并且是在本地的计算机上运行。

最后我们将采用第三种方法来测试api。这里推荐使用Mock Service Worker这个库作为mock server。它使用Service Worker拦截浏览器中的API请求并返回mock数据。

解决了mock数据的问题,接下来需要讲解如何测试组件。

测试react组件

这里采用了react-test-render库来进行组件测试。首先创建一个组件,该组件使用react-query从查询函数中mock获取数字列表。

const fetchNumberList = () => {
  return new Promise(res => setTimeout(res, 100))
  return [1,2,3,4,5];
}


const NumberList = () => {
  const numberQuery = useQuery(["numbers"], fetchNumberList);


  if (numberQuery.isLoading) return <div>Loading...</div>;


  return <div>{numberQuery.data.join(',')}</div>;
}

由于你还需要为每个测试创建独立的queryClient,因此在组件中定义的代码如下:

import {useState} from 'react';
import {QueryClient, QueryClientProvider} from 'react-query';


const TestWrapper = ({children}) => {
  const [client] = useState(()=> new QueryClient());


  return (
    <QueryClientProvider client={client}>
      {children}
    </QueryClientProvider>
  );

下面的代码中使用react-test-renderer来渲染组件。一开始数据还未返回时,将会展示loading,接着等待100ms后,将会把mock生成的数据渲染在dom上。

下面的代码使用node.js的assert.deepEqual函数进行校验,如果检测不通过,将会抛出错误。

import ReactTestRenderer from 'react-test-renderer'
import assert from 'assert';

async function testNumberList() {
  const renderer = ReactTestRenderer.create(
    <TestWrapper>
      <NumberList />
    </TestWrapper>
  );

  assert.deepEqual(renderer.toJSON(), {
    type: 'div',
    props: {},
    children: ['Loading...'],
  });

  // 这里需要注意,等待时间需要大于100ms因为mock数据的生成时间是设置的是等待100ms返回。
  await new Promise((res) => setTimeout(res, 500));

  assert.deepEqual(renderer.toJSON(), {
    children: ['1,2,3,4,5'],
    props: {},
    type: 'div',
  });
}

上面的示例,仅仅只是测试了组件,如果希望测试基于react-query封装的hooks,还需要使用到其它的方法。

测试react hooks

测试react hooks还需要一个另外的测试库React Hooks Testing Library

接下来定义一个封装了useQueryuseNumberList钩子,方便后续重用该功能:

const fetchNumberList = () => {
  return new Promise(res => setTimeout(res, 100))
  return [1,2,3,4,5];
}

function useNumberList() {
  return useQuery("numbers", fetchNumberList);
}

接下来,编写测试代码:

import { renderHook } from '@testing-library/react-hooks'
import assert from 'assert';

async function testNumberList() {  
  // ①
  const { result, waitForNextUpdate } = renderHook(
    () => useNumberList(),
    { wrapper: TestWrapper }
  )

  assert.equal(result.current.isLoading, true);
  assert.equal(result.current.data, undefined);

  await waitForNextUpdate()

  assert.equal(result.current.isLoading, false);
  assert.deepEqual(result.current.data, [1,2,3,4,5]);
} 

①:你需要使用@testing-library/react-hooks中的renderHook方法来渲染钩子,你需要通过使用result来判断钩子的内部状态,waitForNextUpdate来等待钩子获取数据完成。

此时我们完成了对于react hooks的测试工作。

react-query测试配置

react-query的某些配置对于用户来说非常方便,但是对于测试来说就不太友好,比如请求失败重试这个默认项。由于可以在queryclient上设置整个应用的默认值,因此可以直接把该配置项在测试时设置为false

const TestWrapper = ({children}) => {
  const [client] = useState(()=> new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  }));

  return (
    <QueryClientProvider client={client}>
      {children}
    </QueryClientProvider>
  );
}

到这里我们已经在TestWrapper中为每个测试创建了一个新的queryclient,来确保单个测试结果不会影响到其他测试。

如果担心重复创建新客户端的内存开销,可以在每个测试之前调用queryClient.clear()。它会将queryClient重置到初始状态,为每个运行的测试提供干净的状态。


至此react-query手把手教程就已经结束了,我也完成了之前自己立下的flag。由于写作过程比较长,react-query已经从当初第一篇文章发布时的v4beta版本升级到了v5beta版本(O(∩_∩)O哈哈~作者迭代的速度太快),从当初的只支持react变成了支持所有主流前端框架。

不过终于把坑填完了还是很高兴,由于单更导致很多内容实际上并不是非常的连贯,等笔者有时间的时候,打算把这些教程整合一下,这22篇文章整理下来三万字是跑不掉了😭所以需要一段时间来整理,在此之前,就只能麻烦大家凑合着看看了,感谢各位的支持,如果发现文章内有错误,随时在评论区指出,笔者看到后会第一时间勘误的,感谢看到这里的各位同学们。

转载自:https://juejin.cn/post/7206977593588514871
评论
请登录