使用fireEvent.change修改滑块值时出现异常数值的问题排查
问题描述
我想用fireEvent.change修改range输入框的数值,验证用户能否有效调整该值,但测试输出了奇怪的数值,找不到问题原因。
测试代码
import '@testing-library/jest-dom'; import {cleanup,render,screen, fireEvent} from '@testing-library/react'; import Carrier from './carrier'; import Settings from './settings.json' beforeEach(()=>{ cleanup(); }) it(`changes volume level`,()=>{ render(<Carrier settings={Settings}/>); const volume = screen.getByRole(`slider`,{name:`volume`}); expect(Settings.carrier.volume).toEqual(1); fireEvent.change(volume, {target:{value:`0.32`}}); expect(Settings.carrier.volume).toEqual(0.32); })
测试输出
● Rendering › changes volume level expect(received).toEqual(expected) // deep equality Expected: 0.32 Received: -9.89700043360188 49 | 50 | fireEvent.change(volume, {target:{value:`0.32`}}); > 51 | expect(Settings.carrier.volume).toEqual(0.32); | ^ 52 | }) 53 | }); 54 |
组件代码
const Carrier = ({settings}) =>{ function handleWaveformChoice(e){ if(e.target.checked) settings.carrier.type = e.target.value; } function handleVolumeChange(e){ settings.carrier.volume = Math.log10(e.target.value)*20; } return( <> <fieldset> <legend>Waveform</legend> <label> <input value='sine' name='sine' type='radio' onClick={handleWaveformChoice}/> sine </label> <label> <input value='square' name='square' type='radio' onClick={handleWaveformChoice}/> square </label> <label> <input value='triangle' name='triangle' type='radio' onClick={handleWaveformChoice}/> triangle </label> <label> <input value='sawtooth' name='sawtooth' type='radio' onClick={handleWaveformChoice}/> sawtooth </label> </fieldset> <fieldset> <label> <input value={settings.carrier.volume} name='volume' type='range' min="0.0001" step="1" max="1" onChange={handleVolumeChange}/> volume </label> </fieldset> </> ); } export default Carrier;
已尝试方法
- 尝试使用userEvent,确认无类似功能可用
- 尝试将滑块元素断言为HTMLInputElement,但lint提示仅支持TypeScript文件
- 通过console.log确认fireEvent确实修改了滑块值,但仍无法解释异常数值的来源
问题分析与解决
核心问题
你看到的-9.89700043360188不是bug,是组件逻辑的计算结果。看handleVolumeChange函数:
settings.carrier.volume = Math.log10(e.target.value)*20;
当输入值为0.32时,Math.log10(0.32)≈-0.49485,乘以20后正好是≈-9.897,和测试输出数值完全匹配。你的测试预期值写反了——组件是把滑块输入值转成对数分贝值,而不是直接存输入值。
额外问题:range输入的step设置错误
组件里的range输入step="1",但min是0.0001、max是1,step设为1的话,滑块只能取0.0001和1两个值,用户实际操作根本选不到0.32,测试里的fireEvent.change虽然能触发,但不符合真实用户场景。
修复方案
修正测试预期值:按照组件的计算逻辑写预期,用
toBeCloseTo避免浮点数精度问题:expect(Settings.carrier.volume).toBeCloseTo(Math.log10(0.32)*20);修正range的step属性:如果要让滑块能选择0.0001到1之间的任意小数值,把
step改成0.0001或者"any":<input value={settings.carrier.volume} name='volume' type='range' min="0.0001" step="0.0001" max="1" onChange={handleVolumeChange}/>优化状态管理(可选):直接修改props里的
settings是React反模式,props应该只读。正确做法是把settings放到组件state中,或者让父组件传递修改函数:const Carrier = ({initialSettings, onSettingsChange}) =>{ const [settings, setSettings] = useState(initialSettings); function handleVolumeChange(e){ const newVolume = Math.log10(e.target.value)*20; const updatedSettings = {...settings, carrier: {...settings.carrier, volume: newVolume}}; setSettings(updatedSettings); onSettingsChange?.(updatedSettings); } // ...其他代码 }这样测试时可以通过
onSettingsChange回调获取修改后的值,还能避免修改模块级的JSON文件影响其他测试。
内容的提问来源于stack exchange,提问作者wavesinaroom
相关产品推荐
相关产品推荐

