React/Jest场景下如何模拟用户篡改HTML option值编写TDD单元测试
问题原因
你写的测试没有按预期失败,首先是测试本身存在几处语法/逻辑错误:
- 提交按钮的选择器写错:
input[type=select]是无效选择器,你的提交按钮type属性是submit - 代码中
elements.unitSelect是未定义变量,你已经提前查询到了select元素,直接用它访问options即可 - 断言逻辑错误:
jest.fn()的调用记录是累计的,toHaveBeenCalledWith('fr')只会判断历史有没有过调用传fr的记录,你需要判断最后一次提交的参数是不是fr,要用toHaveBeenLastCalledWith,最好提前清空mock的调用历史避免干扰。
修正后的测试代码
it('keeps previous countryId if the selected one has been tampered with', () => { const onSubmit = jest.fn(() => Promise.resolve()) const countries = [ { id: 'fr', label: 'France' }, { id: 'en', label: 'England' }, ] const { container } = render( <CreateTripForm countries={countries} onSubmit={onSubmit} /> ) const select = container.querySelector('select[name=countryId]') const submitButton = container.querySelector('input[type=submit]') // 先选择合法值fr,提交成功 fireEvent.change(select, { target: { value: 'fr' } }) fireEvent.click(submitButton) expect(onSubmit).toHaveBeenLastCalledWith('fr') // 重置mock调用记录,避免历史调用干扰后续判断 onSubmit.mockClear() // 模拟用户篡改option值 select.options[2].value = 'asgard' // 触发change事件,选中被篡改的值 fireEvent.change(select, { target: { value: 'asgard' } }) // 提交,预期还是用之前的合法值fr fireEvent.click(submitButton) expect(onSubmit).toHaveBeenLastCalledWith('fr') })
这时候运行测试会按预期失败,因为现在的组件逻辑会直接把asgard存到countryId里,提交的时候会传asgard。
组件修复逻辑
你只需要在更新state前加一层白名单校验,判断传入的值是不是在合法的国家id列表里即可:
export const CreateTripForm = ({ countries, onSubmit }) => { const [countryId, setCountryId] = useState() // 提前生成合法id的白名单集合 const validCountryIds = new Set(countries.map(c => c.id)) const submit = async (e) => { e.preventDefault() if (countryId === undefined) return await onSubmit(countryId) } return ( <form onSubmit={submit}> <legend>Choose a country</legend> <label htmlFor="countryId">Country</label> <select name="countryId" required value={countryId} onChange={(e) => { const newValue = e.target.value // 只有合法值才更新state if (validCountryIds.has(newValue)) { setCountryId(newValue) } }} > <option value=""></option> {countries.map((country) => <option key={country.id} value={country.id}>{country.label}</option> )} </select> <input type="submit" value="Create a trip" /> </form> ) }
修改后再运行测试,就可以正常通过了。
内容的提问来源于stack exchange,提问作者sonicolasj
相关产品推荐
相关产品推荐

