首页 > 其他分享 >Jest进阶知识:深入测试 React Hooks-确保自定义逻辑的可靠性

Jest进阶知识:深入测试 React Hooks-确保自定义逻辑的可靠性

时间:2024-11-06 21:20:18浏览次数:6  
标签:reset set const 进阶 自定义 Hooks current result useCounter

测试 React Hooks

在 React 开发中,Hooks 是一个非常重要的功能模块,允许开发者在函数组件中使用状态和其他 React 特性。自定义 Hooks 作为一种公共逻辑的抽离,经常被多个组件复用,因此对其测试是非常必要的。

然而,由于 Hooks 必须在组件内部使用,直接测试它们并不像普通函数那样简单。幸运的是,@testing-library/react-hooks 提供了一种简便的方法来测试 React Hooks。

安装和使用 @testing-library/react-hooks

首先,确保你已经安装了 @testing-library/react-hooks

npm install @testing-library/react-hooks
快速上手示例

假设我们有一个自定义 Hook useCounter,用于创建一个计数器:

// 自定义 hook
// 这是一个计数器的自定义 hook
// 内部维护了一个计数的值,以及修改这个值的一些方法

import { useState } from "react";

interface Options {
  min?: number;
  max?: number;
}

type ValueParam = number | ((c: number) => number);

// 该方法主要是做一个边界的判断,如果超过了边界,那么就取边界值
function getTargetValue(val: number, options: Options = {}) {
  const { min, max } = options;
  let target = val;
  // 判断有没有超过最大值,如果超过了,那么我们就取最大值
  if (typeof max === "number") {
    target = Math.min(max, target);
  }
  // 判断有没有超过最小值,如果超过了,那么我们就取最小值
  if (typeof min === "number") {
    target = Math.max(min, target);
  }
  return target;
}

// useCounter(100, {min : 1, max : 1000})
function useCounter(initialValue = 0, options: Options = {}) {
  const { min, max } = options;

  // 设置初始值,初始值就为 initialVaule
  // 初始值是该自定义 hook 内部维护的状态,用来表示计数器的数值
  const [current, setCurrent] = useState(() => {
    return getTargetValue(initialValue, {
      min,
      max,
    });
  });

  // 设置新的值
  // 在设置新的值的时候,调用了 getTargetValue 来判断新值是否越界
  const setValue = (value: ValueParam) => {
    setCurrent((c) => {
      const target = typeof value === "number" ? value : value(c);
      return getTargetValue(target, {
        max,
        min,
      });
    });
  };

  // 下面就是自定义 hook 提供的 4 个方法
  // 用于修改计数器的数值

  // 增加
  const inc = (delta = 1) => {
    setValue((c) => c + delta);
  };

  // 减少
  const dec = (delta = 1) => {
    setValue((c) => c - delta);
  };

  // 设置
  const set = (value: ValueParam) => {
    setValue(value);
  };

  // 重置
  const reset = () => {
    setValue(initialValue);
  };

  // 像外部暴露
  return [
    current,
    {
      inc,
      dec,
      set,
      reset,
    },
  ] as const;
}

export default useCounter;

接下来,我们将对这个自定义 Hook 进行测试。

测试同步操作
import useCounter from "../hooks/useCounter";
import { renderHook, act } from "@testing-library/react";

test("可以做加法", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0));

  // Act(行为)
  act(() => result.current[1].inc(2));

  // Assert(断言)
  expect(result.current[0]).toEqual(2);
});

test("可以做减法", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0));

  // Act(行为)
  act(() => result.current[1].dec(2));

  // Assert(断言)
  expect(result.current[0]).toEqual(-2);
});

test("可以设置值", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0));

  // Act(行为)
  act(() => result.current[1].set(100));

  // Assert(断言)
  expect(result.current[0]).toEqual(100);
});

test("可以重置值", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0));

  // Act(行为)
  act(() => result.current[1].set(100));
  act(() => result.current[1].reset());

  // Assert(断言)
  expect(result.current[0]).toEqual(0);
});

test("可以设置最大值", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0, { max: 100 }));

  // Act(行为)
  act(() => result.current[1].set(1000));

  // Assert(断言)
  expect(result.current[0]).toEqual(100);
});

test("可以设置最小值", () => {
  // Arrange(准备)

  // result ---> {current : [0, {inc, dec, set, reset}]}
  const { result } = renderHook(() => useCounter(0, { min: -100 }));

  // Act(行为)
  act(() => result.current[1].set(-1000));

  // Assert(断言)
  expect(result.current[0]).toEqual(-100);
});
使用自定义 Hook

测试通过后,可以在组件中安全地使用这个自定义 Hook:

import "./App.css";
import useCounter from "./hooks/useCounter";

function App() {
  const [current, { inc, dec, set, reset }] = useCounter(5, { min: 0, max: 10 });
  return (
    <div className="App">
      <div>{current}</div>
      <button onClick={() => dec(1)}>-</button>
      <button onClick={() => inc(1)}>+</button>
      <button onClick={() => set(100)}>set</button>
      <button onClick={() => reset()}>reset</button>
    </div>
  );
}

export default App;
测试异步操作

假设我们在 useCounter 中添加了一个异步的增加方法:

const asyncInc = (delta = 1) => {
  setTimeout(() => {
    setValue((c) => c + delta);
  }, 2000);
};

测试异步操作时,可以使用 jestuseFakeTimersadvanceTimersByTime 方法来模拟时间流逝:

test("测试异步的增加", async () => {
  jest.useFakeTimers();
  const { result } = renderHook(() => useCounter(0));
  act(() => result.current[1].asyncInc(2));
  expect(result.current[0]).toEqual(0); // 初始值未变
  await act(() => jest.advanceTimersByTime(2000)); // 模拟时间流逝
  expect(result.current[0]).toEqual(2); // 值已更新
  jest.useRealTimers();
});

结论

通过本文的介绍,我们了解了如何使用 @testing-library/react-hooks 测试 React Hooks,特别是自定义 Hooks。通过对 Hooks 的行为进行测试,可以确保它们在不同情况下的表现符合预期,从而提高代码的可靠性和可维护性。

标签:reset,set,const,进阶,自定义,Hooks,current,result,useCounter
From: https://blog.csdn.net/weixin_53961451/article/details/143529591

相关文章

  • go语言进阶之同步原语
    同步原语资源竞争定义与实现在Go语言中,资源竞争指多个goroutine同时访问共享资源,导致程序的行为不可预测或者不一致。资源竞争通常发生在对同一变量进行读写操作时,如果没有正确的同步机制来控制访问可能会引发资源竞争packagemainimport("fmt""sync")......
  • Java面向对象进阶学习一
    this关键字使用this使用的变量时属性,没有使用的是形参this可以调用结构,成员变量,方法this的理解,当前对象(在方法中调用时),或当前正在创建的对象(在构造器中调用时)针对方法内的使用情况一般情况下,我们通过对想a调用方法,可以在方法内调用当前对象a的属性或其他的方法,此时,我们可......
  • netdxf中创建一个新的自定义视图
    需求是我想使用netdxf创建一个文件,插入一个长方体块进去,保存文件后打开就是我想要的视图,例如一个等二测的角度谢谢惊惊,南胜,鸿,和尚,大佬们关键1:DxfDocument.Viewport.ViewDirection关键2:向量与大小无关,只要等比例即可获取原图形中的视图角度,使用ifox的代码如下......
  • 鸿蒙开发进阶(HarmonyOS )FileUri开发指南(C/C++)
     鸿蒙NEXT开发实战往期必看文章:一分钟了解”纯血版!鸿蒙HarmonyOSNext应用开发!“非常详细的”鸿蒙HarmonyOSNext应用开发学习路线!(从零基础入门到精通)HarmonyOSNEXT应用开发案例实践总结合(持续更新......)HarmonyOSNEXT应用开发性能优化实践总结(持续更新......)场景介......
  • C++工厂模式全解析:从简单工厂到抽象工厂的进阶之路
    在软件设计中,工厂模式(FactoryPattern)是一类创建型设计模式,用于将对象的创建过程和使用过程解耦。这种设计模式在面向对象编程中非常常见,特别是在构建复杂系统时,工厂模式可以使代码更加灵活、模块化、易于扩展。工厂模式的主要类型包括:简单工厂模式(SimpleFactory)工厂方法模......
  • 鸿蒙开发进阶(HarmonyOS)时域可分层视频编码
     鸿蒙NEXT开发实战往期必看文章:一分钟了解”纯血版!鸿蒙HarmonyOSNext应用开发!“非常详细的”鸿蒙HarmonyOSNext应用开发学习路线!(从零基础入门到精通)HarmonyOSNEXT应用开发案例实践总结合(持续更新......)HarmonyOSNEXT应用开发性能优化实践总结(持续更新......)基础概......
  • QCustomPlot添加自定义的图例,实现隐藏、删除功能(一)
    文章目录实现步骤:实现代码:代码讲解:功能说明:优化建议:其他参考:要实现一个支持勾选并可以控制曲线显示和隐藏的自定义QCPLegend类,可以通过继承QCPLegend并重写其相关方法来实现。我们需要添加一个自定义的复选框元素,并捕捉用户交互来实现曲线的隐藏......
  • Bootstrap Blazor自定义图片预览组件
            BootstrapBlazor的官方虽然有提供图片预览组件ImagePreviewer,但是,它是置于窗口顶层的,而且是全屏显示,如果业务中有在预览组件中添加其它功能的需求,它是不支持扩展的。    为此,我参考官方的源码,自己写了一个自定义图片预览组件,文件的源码在下面,可自行......
  • 鸿蒙开发进阶(OpenHarmony)扩展组件-系统调用
    鸿蒙NEXT开发实战往期必看文章:一分钟了解”纯血版!鸿蒙HarmonyOSNext应用开发!“非常详细的”鸿蒙HarmonyOSNext应用开发学习路线!(从零基础入门到精通)HarmonyOSNEXT应用开发案例实践总结合(持续更新......)HarmonyOSNEXT应用开发性能优化实践总结(持续更新......)基本概念......
  • WinNTSetup 使用教程的框架,您可以根据自己的需求深入研究每个部分,特别是集成驱动、应
    WinNTSetupv5.3.5.2-InstallWindowsfromUSB-MSFNWinNTSetup官方原版多国语言版下载链接:https://www.mediafire.com/folder/53um6k2nmhvd5/https://www.mediafire.com/file/rbpu88tre4nxwbe/WinNTSetup_v5352.rar/fileWinNTSetupv5352初级使用教程大纲引言WinNTSet......