自定义useDebounce防抖输入场景Cypress测试未触发API请求解决方案咨询
问题原因与解决方法
核心问题根源
你自定义的useDebounce不符合React自定义Hook的规范,没有对定时器和回调函数做持久化处理,导致防抖逻辑在组件重渲染时被重置:
- 你当前的实现中,
timer是函数内部的普通变量,每次组件重渲染调用useDebounce时都会生成全新的函数实例和新的timer变量,之前的定时器上下文直接丢失,最终防抖回调永远不会触发,自然不会发起API请求。 - 手动在浏览器操作时因为输入速度快,输入过程中组件没有触发重渲染,所以看起来功能正常;而Cypress逐字符输入的逻辑会频繁触发输入事件,极易导致组件重渲染,直接暴露了这个实现缺陷。
- Lodash的防抖函数你大概率是配合
useCallback/useRef做了持久化处理,或者生成防抖实例的逻辑放在了组件渲染逻辑之外,所以测试可以正常通过。
解决步骤
第一步:修复自定义useDebounce实现
用React的useRef持久化定时器和回调函数,useCallback返回稳定的防抖函数引用,避免重渲染导致的上下文丢失:
import { useRef, useCallback } from 'react'; const useDebounce = (func, timeout = 500) => { // 持久化定时器ID,重渲染不重置 const timerRef = useRef(null); // 持久化最新的回调函数,避免闭包陷阱 const funcRef = useRef(func); funcRef.current = func; // 返回稳定的防抖函数实例 const debouncedFunc = useCallback((...args) => { clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { funcRef.current.apply(this, args); }, timeout); }, [timeout]); return debouncedFunc; }; export default useDebounce;
第二步:调整Cypress测试逻辑(二选一即可)
方案1:直接等待防抖超时
修复防抖实现后,在所有输入操作完成后等待超过防抖时长(你设置的是700ms)的时间即可:
describe('Testing autoload PreForm information [Desktop]', () => { beforeEach(() => { cy.loadingCucaCorpContact(); cy.visit('/'); cy.setViewportXL(); }); it('should display autoloaded information', () => { cy.findAllByText('Despacho y retiro').click(); cy.findAllByText('No ha llegado mi producto').click(); cy.get('[data-cy=inputrutNotReceived]').type('11111111-1'); cy.get('[data-cy=inputorderIdNotReceived]').type('777777777'); // 等待超过700ms保证防抖触发 cy.wait(1000); cy.contains( 'Lo sentimos, estamos presentando algunas dificultades, vuelve a intentarlo dentro de un momento', { timeout: 5000 } ); }); });
方案2:用模拟时钟提速测试
需要注意必须在页面加载、组件初始化之前开启时钟劫持,才能接管组件内的setTimeout逻辑:
describe('Testing autoload PreForm information [Desktop]', () => { beforeEach(() => { cy.loadingCucaCorpContact(); cy.visit('/'); cy.setViewportXL(); }); it('should display autoloaded information', () => { // 先开启时钟劫持,再执行后续操作 cy.clock(); cy.findAllByText('Despacho y retiro').click(); cy.findAllByText('No ha llegado mi producto').click(); cy.get('[data-cy=inputrutNotReceived]').type('11111111-1'); cy.get('[data-cy=inputorderIdNotReceived]').type('777777777'); // 快进1000ms触发防抖回调 cy.tick(1000); cy.contains( 'Lo sentimos, estamos presentando algunas dificultades, vuelve a intentarlo dentro de un momento', { timeout: 5000 } ); }); });
内容的提问来源于stack exchange,提问作者Danilo Sandoval
相关产品推荐
相关产品推荐

