React-Bootstrap注册组件Alert动态显示控制及测试问询
React-Bootstrap 注册组件 Alert 显示控制与测试问题
我用 React-Bootstrap 开发了一个注册组件,表单包含 email、password 输入框和提交按钮,组件末尾有个 Alert 组件,要求初始不可见,提交表单后根据服务器响应显示成功/失败提示。需要解决以下问题:
- 如何初始隐藏 Alert?比如动态设置
display:none? - 如何在收到服务器响应后让 Alert 可见?
同时需要编写测试用例:
- 验证 Alert 初始不在文档中(现有测试代码是否正确?)
- 模拟服务器响应后,验证 Alert 可见且包含正确提示消息(如何实现?)
组件代码(原版本)
import React from 'react'; import {Col, Row, Container, Card,Button, Alert} from 'react-bootstrap' import Form from 'react-bootstrap/Form'; import { useState } from 'react'; import User from '../models/user'; let Register = () => { let [state,setState] = useState(new User('','')); let submitresponse=''; let alertvariant=''; let updateInput = (e) =>{ setState({ ...state, //state defined above in useState call. Expand the current state and then override the propoerty which target represents [e.target.name] : e.target.value }) } let register = (e) =>{ e.preventDefault(); console.log(`registerclick: ${JSON.stringify(state)}`) fetch(process.env.REACT_APP_REGISTER_USER_URL,{ method: 'POST', headers:{ 'Content-Type': 'application/json' }, body: JSON.stringify(state) }) //move to separate funcion in separate file to make it easy to test and reuse? .then((res) => { console.log(`got response: ${JSON.stringify(res)}`); return res.json(); //return json from res }) .then((data) => { //data is the json returned previously. Promise chaining console.log(`data after register user request: ${JSON.stringify(data)}`); if(data.result == "success") { setState(new User(data.user,data.email)) //override previous value of data object with value of received json object. this.submitresponse = "User Registered" } else { setState(new User('','')) //override previous value of data object with value of received json object. this.submitresponse = "User Couldn't be Registered" } }); } return ( <> <Container className='mt-3'> <Row> <Col xs={3}> <Card className="shadow-lg"> <Card.Header className='p-3'> Register </Card.Header> <Card.Body> <Form> <Form.Group className="mb-3"> <Form.Control name="email" onChange={updateInput} type="text" placeholder="email" aria-label="aria-email" /> </Form.Group> <Form.Group className="mb-3"> <Form.Control name="password" onChange={updateInput} type="password" placeholder="password" aria-label="aria-password" /> </Form.Group> <Form.Group className="mb-3"> <Button onClick={register} type="submit" name="submit-button" aria-label="aria-submit">Register Button</Button> </Form.Group> <Form.Group className="mb-3"> <Alert name="submit-response-alert" key={this.alertvariant} variant={this.alertvariant} >{this.submitresponse}</Alert> </Form.Group> </Form> </Card.Body> </Card> </Col> </Row> </Container> </> ) } export default Register;
现有测试代码片段
1. 初始 Alert 验证
test('Should not show Alert field', async () => { render(); expect(screen.getByName("submit-response-alert")).not.toBeInTheDocument(); //Is this the right way? });
2. 提交后 Alert 验证
test('Should successfully submit the form', async () => { const { getByText } = render(); await act(async () => { fireEvent.click(getByText("Register Button")) }) expect(screen.getByName("submit-response-alert")).toBeInTheDocument(); //how to test that the Alert has the right message });
问题解答与代码修正
一、Alert 显示/隐藏的组件代码修正
原组件存在两个核心问题:函数组件误用 this 访问变量、Alert 未做显示控制。以下是修正后的完整代码:
import React, { useState } from 'react'; import { Col, Row, Container, Card, Button, Alert } from 'react-bootstrap'; import Form from 'react-bootstrap/Form'; import User from '../models/user'; const Register = () => { const [state, setState] = useState(new User('', '')); // 新增状态控制 Alert 的显示状态、内容和样式 const [alertState, setAlertState] = useState({ show: false, message: '', variant: '' }); const updateInput = (e) => { setState({ ...state, [e.target.name]: e.target.value }); }; const register = (e) => { e.preventDefault(); console.log(`registerclick: ${JSON.stringify(state)}`); fetch(process.env.REACT_APP_REGISTER_USER_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(state) }) .then((res) => res.json()) .then((data) => { console.log(`data after register user request: ${JSON.stringify(data)}`); if (data.result === "success") { setState(new User(data.user, data.email)); setAlertState({ show: true, message: "User Registered", variant: 'success' }); } else { setState(new User('', '')); setAlertState({ show: true, message: "User Couldn't be Registered", variant: 'danger' }); } }) .catch(err => { // 处理请求失败场景 setAlertState({ show: true, message: "请求失败,请稍后重试", variant: 'danger' }); }); }; return ( <Container className='mt-3'> <Row> <Col xs={3}> <Card className="shadow-lg"> <Card.Header className='p-3'>Register</Card.Header> <Card.Body> <Form onSubmit={register}> <Form.Group className="mb-3"> <Form.Control name="email" onChange={updateInput} type="email" placeholder="email" aria-label="aria-email" /> </Form.Group> <Form.Group className="mb-3"> <Form.Control name="password" onChange={updateInput} type="password" placeholder="password" aria-label="aria-password" /> </Form.Group> <Form.Group className="mb-3"> <Button type="submit" name="submit-button" aria-label="aria-submit"> Register Button </Button> </Form.Group> {/* 条件渲染控制 Alert 显示:初始不渲染,响应后渲染 */} {alertState.show && ( <Form.Group className="mb-3"> <Alert name="submit-response-alert" variant={alertState.variant} > {alertState.message} </Alert> </Form.Group> )} </Form> </Card.Body> </Card> </Col> </Row> </Container> ); }; export default Register;
关键说明:
初始隐藏 Alert:
- 通过
alertState.show做条件渲染,初始show: false时不会渲染 Alert,自然不在 DOM 中 - 若需保留 DOM 仅隐藏,可改用样式控制:
<Alert style={{ display: alertState.show ? 'block' : 'none' }} ... />,但条件渲染更高效
- 通过
响应后显示 Alert:
- 在 fetch 的回调中,根据服务器返回结果调用
setAlertState,将show设为true,同时设置对应提示消息和样式变体(success/danger)
- 在 fetch 的回调中,根据服务器返回结果调用
二、测试用例修正与实现
1. 验证 Alert 初始不在文档中
原测试代码错误:screen.getByName 不是 React Testing Library 标准方法,且初始不存在的元素应使用 queryBy(getBy 找不到会报错)。修正后:
import { render, screen } from '@testing-library/react'; import Register from './Register'; test('初始状态下不显示 Alert', () => { render(<Register />); // queryByRole 找不到元素返回 null,不会抛出错误 const alert = screen.queryByRole('alert'); expect(alert).not.toBeInTheDocument(); });
2. 模拟服务器响应后验证 Alert 状态
需先模拟 fetch 请求避免调用真实 API,再触发提交并验证 Alert:
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import Register from './Register'; // 模拟成功响应 test('提交成功后显示成功提示的 Alert', async () => { global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ result: "success", user: "testUser", email: "test@example.com" }) }) ); render(<Register />); fireEvent.click(screen.getByText('Register Button')); // 等待异步操作完成,验证 Alert 状态 await waitFor(() => { const successAlert = screen.getByRole('alert'); expect(successAlert).toBeInTheDocument(); expect(successAlert).toHaveTextContent('User Registered'); expect(successAlert).toHaveClass('alert-success'); }); jest.restoreAllMocks(); }); // 模拟失败响应 test('提交失败后显示失败提示的 Alert', async () => { global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ result: "fail" }) }) ); render(<Register />); fireEvent.click(screen.getByText('Register Button')); await waitFor(() => { const errorAlert = screen.getByRole('alert'); expect(errorAlert).toBeInTheDocument(); expect(errorAlert).toHaveTextContent("User Couldn't be Registered"); expect(errorAlert).toHaveClass('alert-danger'); }); jest.restoreAllMocks(); });
测试关键说明:
- 用
jest.fn()模拟global.fetch,返回预设的成功/失败响应 - 使用
waitFor等待异步请求完成,确保 Alert 已渲染 - 通过
getByRole('alert')获取元素,验证其存在性、文本内容和样式类
内容的提问来源于stack exchange,提问作者Manu Chadha
相关产品推荐
相关产品推荐

