医院预约挂号平台web管理端用例设计及自动化测试实践
一.项目介绍
该项目是⼀个用于地区医院就诊挂号场景的微信小程序。项目由小程序用户端、Web管理端、后台服务三部分组成:微信小程序用户端有注册登录、绑定就诊卡、预约挂号、信用评价等模块。管理Web端按角色区分,管理员可以维护医院、科室、诊室和医生信息,给医生排班等;医生角色可以查看患者信息、出诊信息等。
二.测试用例设计


三.登录模块功能测试&自动化测试
测试用例

| 用例模块 | 用例名 | 用户 | 密码 | 预期结果 |
|---|---|---|---|---|
| 登录 | 正确账号和密码 | admin | admin | 登录成功,跳转到首页 |
| 登录 | 账号为空 | 空 | admin | 提示"请输入账号" |
| 登录 | 密码为空 | admin | 空 | 提示"请输入密码" |
| 登录 | 账号密码均为空 | 空 | 空 | 点击登录按钮无响应 |
| 登录 | 账号错误密码正确 | admintest | admin | 提示"用户名或密码错误!" |
| 登录 | 账号正确密码错误 | admin | adminerror | 提示"用户名或密码错误!" |
功能测试
1.正确账号和密码
成功跳转到首页

2.账号为空
提示"请输入账号"

3.密码为空
提示"请输入密码"

4.账号密码均为空
点击登录按钮无响应,提示"请输入账号" &"请输入密码"

5.账号错误密码正确
提示"用户名或密码错误!"

6.账号正确密码错误
提示"用户名或密码错误!"

自动化测试
1.整体结构
点击查看代码
pytestdemo/
├── test_login.py # 登录功能测试用例
├── test_data.yml # 测试数据文件
├── conftest.py # pytest 配置与 fixture
├── allure-results/ # allure 测试结果数据
├── *.png # 测试过程截图
├── __pycache__/ # Python 缓存文件夹
└── .pytest_cache/ # pytest 缓存文件夹
2.自动化测试实现代码 test_login.py
点击查看代码
import pytest
import allure
import yaml
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time # 用于等待
# 读取 YAML 测试数据
def load_test_data():
with open("test_data.yml", encoding="utf-8") as f:
return yaml.safe_load(f)['login_cases']
# 处理空值
def process_empty(value):
return "" if value == "空" else value
# 定位器解析函数
def parse_locator(locator_str):
if locator_str.startswith("//"):
return By.XPATH, locator_str
elif locator_str.startswith("."):
return By.CSS_SELECTOR, locator_str
else:
raise ValueError(f"不支持的定位器格式:{locator_str}")
@allure.feature("登录功能测试")
class TestLogin:
@pytest.mark.parametrize("case", load_test_data())
def test_login(self, driver, case):
with allure.step("打开登录页面"):
driver.get("http://localhost:8081/#/")
# 输入账号
username_input = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.XPATH, "//input[@placeholder='账号']"))
)
with allure.step("点击账号输入框"):
username_input.click()
# 使用封装方法清空账号输入框
self.clear_input_field(driver, username_input)
username = process_empty(case["username"])
with allure.step(f"输入账号:{username}"):
username_input.send_keys(username)
time.sleep(0.5)
# 输入密码
password_input = driver.find_element(By.XPATH, "//input[@placeholder='密码']")
with allure.step("点击密码输入框"):
password_input.click()
# 使用封装方法清空密码输入框
self.clear_input_field(driver, password_input)
password = process_empty(case["password"])
with allure.step(f"输入密码:{password}"):
password_input.send_keys(password)
time.sleep(0.5)
# 获取登录按钮
login_button = driver.find_element(By.CSS_SELECTOR, ".el-button.el-button--primary")
with allure.step("点击登录按钮"):
expected_result = case["expected_result"]
if "登录按钮为禁用状态" in expected_result:
with allure.step("验证登录按钮禁用状态"):
try:
assert not login_button.is_enabled(), "登录按钮未被禁用"
except AssertionError as e:
self._take_screenshot_and_attach(driver, case, "错误截图")
raise e
else:
login_button.click()
# 点击登录后等待提示信息出现
if "提示" in expected_result:
self._wait_for_error_messages(driver, case)
time.sleep(1) # 添加这一行,等待1秒
# 点击登录后截图
self._take_screenshot_and_attach(driver, case, "点击登录后页面状态")
if "跳转到首页" in expected_result:
expected_url = case["expected_url"]
with allure.step(f"验证跳转地址:{expected_url}"):
try:
WebDriverWait(driver, 10).until(
EC.url_to_be(expected_url)
)
actual_url = driver.current_url
assert actual_url == expected_url, f"预期地址:{expected_url},实际:{actual_url}"
except AssertionError as e:
self._take_screenshot_and_attach(driver, case, "错误截图")
raise e
time.sleep(0.5)
elif "提示" in expected_result:
# 判断是单个提示还是多个提示
if "tip_locators" in case:
# 多个提示信息
tip_locators = case.get("tip_locators", [])
tip_texts = case.get("tip_texts", [])
for locator_str, expected_text in zip(tip_locators, tip_texts):
locator_type, locator_value = parse_locator(locator_str)
with allure.step(f"验证提示信息:{expected_text}"):
try:
tip_element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((locator_type, locator_value))
)
actual_text = tip_element.text
assert expected_text in actual_text, f"预期提示:{expected_text},实际:{actual_text}"
except AssertionError as e:
self._take_screenshot_and_attach(driver, case, "错误截图")
raise e
time.sleep(1)
else:
# 单个提示信息
tip_locator = case.get("tip_locator")
tip_text = case.get("tip_text")
locator_type, locator_value = parse_locator(tip_locator)
with allure.step(f"验证提示信息:{tip_text}"):
try:
tip_element = WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((locator_type, locator_value))
)
actual_text = tip_element.text
assert tip_text in actual_text, f"预期提示:{tip_text},实际:{actual_text}"
except AssertionError as e:
self._take_screenshot_and_attach(driver, case, "错误截图")
raise e
time.sleep(0.5)
def clear_input_field(self, driver, element, max_attempts=3):
"""
使用全选 + BACKSPACE 清空输入框内容
"""
with allure.step("清空输入框(全选+BACKSPACE)"):
for attempt in range(max_attempts):
# 点击输入框并全选内容
actions = ActionChains(driver)
actions.click(element)
actions.key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL)
actions.perform()
# 使用 BACKSPACE 删除内容
for _ in range(10): # 尝试最多删除10次
actions = ActionChains(driver)
actions.send_keys(Keys.BACKSPACE)
actions.perform()
# 验证输入框是否为空
current_value = element.get_attribute("value")
if current_value == "":
break
else:
raise TimeoutException("输入框清空失败,尝试次数已达上限")
# 验证输入框内容是否为空
current_value = element.get_attribute("value")
assert current_value == "", f"输入框未正确清空,当前值:'{current_value}'"
def _wait_for_error_messages(self, driver, case):
"""
点击登录后,等待提示信息出现
"""
if "tip_locators" in case:
tip_locators = case.get("tip_locators", [])
for locator_str in tip_locators:
locator_type, locator_value = parse_locator(locator_str)
try:
WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((locator_type, locator_value))
)
except TimeoutException:
pass # 可选处理
else:
tip_locator = case.get("tip_locator")
locator_type, locator_value = parse_locator(tip_locator)
try:
WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((locator_type, locator_value))
)
except TimeoutException:
pass
def _take_screenshot_and_attach(self, driver, case, name="截图"):
"""
截图并附加到 Allure 报告中
"""
file_name = f"{name}_{case['case_name']}.png"
driver.save_screenshot(file_name)
time.sleep(0.5)
allure.attach.file(file_name, name=name, attachment_type=allure.attachment_type.PNG)
3.测试报告

小结
1.测试过程中使用clear()或send_keys("")清空输入框和传入空字符串后,前端验证逻辑未触发,导致测试用例误判(如账号为空却能登录成功使用)。
解决方案:
使用ActionChains模拟真实用户行为 (全选并删除内容)。
点击查看代码
actions = ActionChains(driver)
actions.click(password_input) # 再次点击确保聚焦
actions.key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL) # 全选
actions.send_keys(Keys.BACKSPACE) # 删除内容
actions.perform()
2.点击登录按钮后,前端未执行验证逻辑,导致提示信息未出现。
解决方案:
使用WebDriverWait等待验证结果 (如等待提示信息出现)。
WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.XPATH, "//div[text()='请输入账号']")) )

浙公网安备 33010602011771号