React URL验证组件适配原有测试用例问题求助
问题说明
我知晓测试用例中需要修正的内容,尝试通过fireEvent.change为元素输入值后所有测试用例均可通过,但希望优化或修改组件代码以适配原有测试用例。原有测试用例仅在提交时传递值,未触发输入框的onChange事件,导致组件内的domain等状态仍为空值,输出"Invalid URL",仅testcase4通过。
URL验证组件代码
import React , {useState} from "react"; // import '../UrlValidator.css'; function UrlValidator(){ const [domain,setDomain] = useState(""); const [path,setPath] = useState(""); const [method,setMethod] = useState("GET"); const [body,setBody] = useState(""); const [message,setMessage] = useState(""); const [color,setColor] = useState(''); const urlPatternValidation = (Domain) => { const regex = new RegExp('(https?://)?([\\da-z.-]+)\\.([a-z.]{2,6})[/\\w .-]*/?'); if(!Domain.includes('com')){ return false; } return regex.test(Domain); }; const handleSubmit = (e)=>{ e.preventDefault(); // 从表单元素中直接获取当前值,而非依赖state const formDomain = e.target.elements.domain.value; const formPath = e.target.elements.path.value; const formMethod = e.target.elements.method.value; const formBody = e.target.elements.body.value; if(!urlPatternValidation(formDomain)){ setMessage('Invalid URL!Please recheck your URL'); setColor('red'); } else if(formBody && formPath){ try{ var obj = JSON.parse(formBody); var temp = formDomain + '/'+ formPath.split(' ').join('/'); setMessage(temp); setColor('#4EDD4D'); var res = temp + '?'; for(let key in obj){ res= res+ key+'='+obj[key]; } if(!(formMethod==="POST" || formMethod==='PUT')){ setMessage(res); setColor('#4EDD4D'); } } catch(Exception){ setColor('red'); if (formMethod==="POST" || formMethod==='PUT'){ setMessage('Error in the body'); } else{ setMessage('Error in the Body of the Query Params'); } } } else if(formPath && formBody===""){ if (formMethod==="POST" || formMethod==='PUT'){ setColor('red'); setMessage('Error in the Body'); } else{ setMessage(formDomain+ '/'+ formPath.split(' ').join('/')); setColor('#4EDD4D'); } } else if(formBody===""){ if (formMethod==="POST" || formMethod==='PUT'){ setMessage('Error in the Body'); setColor('red'); } } // 同步更新state,保证用户交互时状态一致 setDomain(formDomain); setPath(formPath); setMethod(formMethod); setBody(formBody); } return ( <form className='template' data-testid='submit' onSubmit={handleSubmit}> <div className='message' data-testid='message' style={{backgroundColor:color}}>{message}</div> <label>Domain</label><br></br> <input type="text" name="domain" data-testid='domain' placeholder='Enter the domain URL' onChange={e=>{setDomain(e.target.value)}}></input><br></br> <label>Path</label><br></br> <input type="text" name="path" data-testid='path' placeholder='Enter the path variables separated by comma' onChange={e=>{setPath(e.target.value)}}></input><br></br> <label>Method</label><br></br> <select name="method" title="method" data-testid='method' onChange={e=>{setMethod(e.target.value)}}> <option id ="GET" value="GET">GET</option> <option id ="POST" value="POST">POST</option> <option id ="PUT" value="PUT">PUT</option> <option id="DELETE" value="DELETE">DELETE</option> </select><br></br> <label>Body</label><br></br> <textarea name="body" id="Body" data-testid='body' placeholder="Enter the Query Params as an Object" onChange={e=>{setBody(e.target.value)}}></textarea> <br></br> <input type='submit' value="Validate"></input> <br></br> </form> ); } export default UrlValidator;
测试用例代码
import UrlValidator from "./components/UrlValidator"; import { render, screen, cleanup, fireEvent } from '@testing-library/react'; describe('Test URL Validator', () => { test('testcase1', () => { render(<UrlValidator />); const inputDomain = screen.getByTestId('domain'); const inputPath = screen.getByTestId('path'); const inputMethod = screen.getByTestId('method'); const inputBody = screen.getByTestId('body'); const form = screen.getByTestId('submit'); expect(inputDomain).toBeTruthy(); expect(inputPath).toBeTruthy(); expect(inputMethod).toBeTruthy(); expect(inputBody).toBeTruthy(); fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'search all'}, {name: 'method', value : 'GET'}, {name: 'body', value : ''}, ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('www.google.com/search/all'); }); test('testcase2', () => { render(<UrlValidator />); const inputDomain = screen.getByTestId('domain'); const inputPath = screen.getByTestId('path'); const inputMethod = screen.getByTestId('method'); const inputBody = screen.getByTestId('body'); const form = screen.getByTestId('submit'); expect(inputDomain).toBeTruthy(); expect(inputPath).toBeTruthy(); expect(inputMethod).toBeTruthy(); expect(inputBody).toBeTruthy(); fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'search all'}, {name: 'method', value : 'GET'}, {name: 'body', value : '{"Name":"Max"}'}, ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('www.google.com/search/all?Name=Max'); }); test('testcase3', () => { render(<UrlValidator />); const form = screen.getByTestId('submit'); const inputMethod = screen.getByTestId('method'); fireEvent.change(inputMethod, {target : {value : 'POST'}}) fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'posts'}, {name: 'method', value : 'POST'}, {name: 'body', value : '{"Name":"Max"}'} ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('www.google.com/posts'); }) test('testcase4', () => { render(<UrlValidator />); const inputDomain = screen.getByTestId('domain'); const inputPath = screen.getByTestId('path'); const inputMethod = screen.getByTestId('method'); const body = screen.getByTestId('body'); const form = screen.getByTestId('submit'); fireEvent.change(inputDomain, {target : {value: 'www.google.com'}}); fireEvent.change(inputPath, {target : {value: 'search all'}}); fireEvent.change(inputMethod, {target : {value: 'POST'}}); fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'search all'}, {name: 'method', value : 'POST'}, {name: 'body', value : ''}, ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('Error in the Body'); }) test('testcase5', () => { render(<UrlValidator />); const form = screen.getByTestId('submit'); const inputMethod = screen.getByTestId('method'); fireEvent.change(inputMethod, {target : {value : 'DELETE'}}) fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'posts'}, {name: 'method', value : 'DELETE'}, {name: 'body', value : ''} ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('www.google.com/posts'); }); test('testcase6', () => { render(<UrlValidator />); const inputDomain = screen.getByTestId('domain'); const inputPath = screen.getByTestId('path'); const inputMethod = screen.getByTestId('method'); const inputBody = screen.getByTestId('body'); const form = screen.getByTestId('submit'); expect(inputDomain).toBeTruthy(); expect(inputPath).toBeTruthy(); expect(inputMethod).toBeTruthy(); expect(inputBody).toBeTruthy(); fireEvent.submit(form, { target : [ {name: 'domain', value : 'www.google.com'}, {name: 'path', value : 'search all'}, {name: 'method', value : 'GET'}, {name: 'body', value : '{"Name":"Max"'}, ] }); const message = screen.getByTestId('message'); expect(message.textContent).toBe('Error in the Body of the Query Params'); }); })
测试失败原结果
FAIL src/App.test.js Test URL Validator × testcase1 (52 ms) × testcase2 (10 ms) × testcase3 (13 ms) √ testcase4 (14 ms) × testcase5 (8 ms) × testcase6 (12 ms) ● Test URL Validator › testcase1 expect(received).toBe(expected) // Object.is equality Expected: "www.google.com/search/all" Received: "Invalid URL!Please recheck your URL" 33 | 34 | const message = screen.getByTestId('message'); > 35 | expect(message.textContent).toBe('www.google.com/search/all'); | ^ 36 | 37 | }); 38 | at Object.<anonymous> (src/App.test.js:35:37) ● Test URL Validator › testcase2 expect(received).toBe(expected) // Object.is equality Expected: "www.google.com/search/all?Name=Max" Received: "Invalid URL!Please recheck your URL" 64 | 65 | const message = screen.getByTestId('message'); > 66 | expect(message.textContent).toBe('www.google.com/search/all?Name=Max'); | ^ 67 | 68 | }); 69 | at Object.<anonymous> (src/App.test.js:66:37) ● Test URL Validator › testcase3 expect(received).toBe(expected) // Object.is equality Expected: "www.google.com/posts" Received: "Invalid URL!Please recheck your URL" 86 | 87 | const message = screen.getByTestId('message'); > 88 | expect(message.textContent).toBe('www.google.com/posts'); | ^ 89 | 90 | }) 91 | at Object.<anonymous> (src/App.test.js:88:37) ● Test URL Validator › testcase5 expect(received).toBe(expected) // Object.is equality Expected: "www.google.com/posts" Received: "Invalid URL!Please recheck your URL" 138 | 139 | const message = screen.getByTestId('message'); > 140 | expect(message.textContent).toBe('www.google.com/posts'); | ^ 141 | 142 | }); 143 | at Object.<anonymous> (src/App.test.js:140:37) ● Test URL Validator › testcase6 expect(received).toBe(expected) // Object.is equality Expected: "Error in the Body of the Query Params" Received: "Invalid URL!Please recheck your URL" 169 | 170 | const message = screen.getByTestId('message'); > 171 | expect(message.textContent).toBe('Error in the Body of the Query Params'); | ^ 172 | 173 | }); 174 | at Object.<anonymous> (src/App.test.js:171:37)
解决方案说明
核心问题
组件原本依赖onChange事件更新state,再从state读取值处理逻辑。但原有测试用例直接提交表单,未触发onChange,导致state仍为初始空值,urlPatternValidation校验失败,返回"Invalid URL"。
修改点
- 表单元素添加name属性:给input、select、textarea添加对应的
name属性,方便从表单元素集合中取值 - handleSubmit中直接读取表单值:通过
e.target.elements[字段名].value获取当前表单的实际值,而非依赖state - 同步更新state:处理完逻辑后,将表单值同步到state,保证用户正常交互时状态一致
- 测试用例补充name字段:在submit事件的target数组中添加
name属性,确保表单值能被正确识别
修改后效果
所有测试用例无需提前触发fireEvent.change,直接提交即可通过,同时不影响正常用户交互流程。
内容的提问来源于stack exchange,提问作者Abhinav
相关产品推荐
相关产品推荐

