react-query手把手教程22-单元测试
场景
在日常的开发过程中,测试对于代码质量的保证是非常重要的。一般团队中都会配备一名或几名测试工程师,手动测试当前的代码中是否正常。但是一旦应用越来越复杂的时候,此时自动化测试的重要性就会提上日程。
本文主要介绍的是如何在自动化测试脚本中使用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
接下来定义一个封装了useQuery
的useNumberList
钩子,方便后续重用该功能:
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