博客系统 Web 自动化测试项目报告
一、项目概述
1.1 项目名称
博客系统 Web 自动化测试项目
1.2 项目类型
Web UI 自动化测试项目
1.3 项目背景
本项目针对一个基于浏览器访问的博客管理系统进行自动化测试,主要验证用户登录、博客列表查看、博客详情查看以及博客发布等核心业务功能。
项目使用 Python 编写测试脚本,基于 Selenium WebDriver 驱动 Chrome 浏览器,模拟真实用户在网页中的操作流程,并通过元素定位、页面跳转、文本断言、弹窗处理等方式验证系统功能是否符合预期。
该项目主要用于熟悉以下测试开发工作:
- Web 页面元素定位;
- 浏览器自动化操作;
- 用户登录流程测试;
- 正向和反向测试用例设计;
- 页面跳转验证;
- 博客发布流程验证;
- JavaScript 编辑器交互处理;
- 浏览器弹窗处理;
- 测试截图保存;
- 测试结果分析和缺陷定位。
1.4 项目目标
本项目的主要目标如下:
- 验证博客系统的核心功能是否能够正常使用;
- 验证错误账号或错误密码时系统是否给出正确提示;
- 验证登录成功后页面是否正确跳转;
- 验证博客列表页面是否能够正常加载;
- 验证博客详情内容是否正确展示;
- 验证用户是否可以成功发布博客;
- 验证博客发布后的成功提示和页面跳转;
- 提高回归测试效率,减少人工重复操作;
- 积累 Selenium Web 自动化测试实践经验。
二、项目环境
2.1 测试环境
| 项目 | 内容 |
|---|---|
| 操作系统 | Windows |
| 开发工具 | PyCharm |
| 编程语言 | Python |
| 自动化框架 | Selenium WebDriver |
| 浏览器 | Google Chrome |
| 浏览器驱动 | ChromeDriver |
| Python 环境 | Python 3.11 |
| 测试对象 | 博客系统 Web 页面 |
| 页面协议 | HTTP |
| 测试地址 | http://47.108.157.13:8090 |
| 项目目录 | pythonProject2 |
| 测试目录 | tests |
2.2 主要依赖
项目主要使用以下技术和库:
selenium
核心 Selenium 模块包括:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
各模块作用如下:
| 模块 | 作用 |
|---|---|
webdriver | 启动和控制 Chrome 浏览器 |
By | 提供元素定位方式 |
WebDriverWait | 实现显式等待 |
expected_conditions | 判断页面元素、弹窗和页面状态是否满足条件 |
time | 在特殊场景下进行短暂等待 |
execute_script | 通过 JavaScript 操作复杂页面元素 |
三、被测系统功能分析
博客系统主要包含以下功能模块:
3.1 登录功能
用户通过登录页面输入用户名和密码,点击登录按钮进入博客系统。
测试内容包括:
- 正确用户名和正确密码登录;
- 错误密码登录;
- 登录成功后的页面跳转;
- 登录失败时的错误提示;
- 登录状态下的页面访问权限。
3.2 博客列表功能
登录成功后,系统跳转到博客列表页面。
测试内容包括:
- 博客列表页面能否正常访问;
- 博客标题是否正常显示;
- 博客列表页面的基本布局是否正常;
- 博客数据是否能够加载;
- 页面跳转地址是否正确。
3.3 博客详情功能
用户从博客列表进入某篇博客详情页,查看博客完整内容。
测试内容包括:
- 详情页是否能够正常打开;
- 博客标题是否正确;
- 博客正文是否正确;
- 列表页和详情页之间的跳转是否正常。
3.4 博客发布功能
登录用户进入博客编辑页面,填写博客标题和正文,点击“发布文章”按钮完成博客发布。
测试内容包括:
- 博客标题输入;
- 博客正文输入;
- Markdown 编辑器交互;
- 发布按钮点击;
- 发布成功弹窗;
- 发布后页面跳转;
- 新博客是否出现在博客列表中;
- 发布博客标题是否正确。
3.5 注销功能
系统页面提供注销入口,用于退出当前登录状态。
测试内容包括:
- 点击注销按钮;
- 页面是否返回登录页;
- 登录状态是否被清除;
- 注销后是否还能直接访问需要登录的页面。
四、自动化测试框架设计
4.1 项目目录结构
项目目录可以整理为以下形式:
pythonProject2
│
├─ tests
│ ├─ BlogDriver.py
│ ├─ BlogLogin.py
│ ├─ BlogList.py
│ ├─ BlogDetail.py
│ ├─ BlogEdit.py
│ └─ RunTest.py
│
├─ screenshots
│ └─ ...
│
└─ .venv
各文件职责如下:
| 文件 | 职责 |
|---|---|
RunTest.py | 测试执行入口,统一调用各个测试方法 |
BlogDriver.py | 浏览器驱动初始化、浏览器关闭、截图等公共操作 |
BlogLogin.py | 登录相关测试 |
BlogList.py | 博客列表页面测试 |
BlogDetail.py | 博客详情页面测试 |
BlogEdit.py | 博客发布页面测试 |
screenshots | 保存测试过程截图 |
4.2 测试执行流程
整体测试流程如下:
启动浏览器
↓
进入博客系统
↓
执行错误密码登录测试
↓
验证登录失败提示
↓
执行正确账号登录
↓
验证跳转到博客列表页
↓
执行博客列表测试
↓
执行博客详情测试
↓
进入博客编辑页
↓
填写博客标题和正文
↓
点击发布按钮
↓
处理“博客发表成功”弹窗
↓
验证跳转到博客列表页
↓
验证新博客标题
↓
保存截图
↓
结束测试
五、测试用例设计
5.1 登录模块测试用例
| 用例编号 | 测试功能 | 测试步骤 | 预期结果 |
|---|---|---|---|
| LOGIN-001 | 正确账号登录 | 输入正确用户名和密码,点击登录 | 登录成功并跳转到博客列表页 |
| LOGIN-002 | 错误密码登录 | 输入正确用户名和错误密码,点击登录 | 页面提示“密码不正确” |
| LOGIN-003 | 错误用户名登录 | 输入不存在的用户名和密码 | 页面提示登录失败 |
| LOGIN-004 | 用户名为空 | 不输入用户名,点击登录 | 页面提示用户名不能为空或阻止提交 |
| LOGIN-005 | 密码为空 | 输入用户名,不输入密码 | 页面提示密码不能为空或阻止提交 |
| LOGIN-006 | 登录成功页面验证 | 登录成功后获取当前 URL | URL 应包含 blog_list.html |
| LOGIN-007 | 登录失败页面验证 | 使用错误密码登录 | 页面不应进入博客列表页 |
5.1.1 错误密码测试
错误密码场景属于反向测试,主要验证系统是否能够正确识别非法登录信息。
示例断言:
actual = self.driver.find_element(
By.CSS_SELECTOR,
"body"
).text
assert "密码不正确" in actual
该测试可以防止以下问题:
- 错误密码也能登录;
- 错误提示不显示;
- 错误提示内容错误;
- 页面跳转到错误页面;
- 前端校验失效。
5.2 博客列表模块测试用例
| 用例编号 | 测试功能 | 测试步骤 | 预期结果 |
|---|---|---|---|
| LIST-001 | 打开博客列表 | 登录成功后进入列表页 | 页面正常加载 |
| LIST-002 | 页面地址验证 | 获取当前页面 URL | URL 包含 blog_list.html |
| LIST-003 | 博客标题验证 | 获取第一篇博客标题 | 标题正常显示 |
| LIST-004 | 博客列表内容验证 | 检查列表区域文本 | 页面存在博客内容 |
| LIST-005 | 博客跳转详情 | 点击博客标题 | 成功进入博客详情页 |
| LIST-006 | 页面基本元素验证 | 查找导航、列表和博客标题 | 页面核心元素存在 |
示例页面地址断言:
print("当前页面地址:", self.driver.current_url)
assert "blog_list.html" in self.driver.current_url
5.3 博客详情模块测试用例
| 用例编号 | 测试功能 | 测试步骤 | 预期结果 |
|---|---|---|---|
| DETAIL-001 | 打开博客详情 | 在列表页点击博客 | 详情页正常打开 |
| DETAIL-002 | 详情标题验证 | 获取详情页标题 | 标题与列表页一致 |
| DETAIL-003 | 正文内容验证 | 获取博客正文 | 正文正常显示 |
| DETAIL-004 | 页面地址验证 | 获取当前 URL | URL 包含详情页地址 |
| DETAIL-005 | 返回列表页 | 点击返回或主页 | 返回博客列表页 |
详情页测试重点在于验证页面之间的数据一致性。博客列表页展示的标题,应该与详情页展示的标题一致。
5.4 博客发布模块测试用例
| 用例编号 | 测试功能 | 测试步骤 | 预期结果 |
|---|---|---|---|
| EDIT-001 | 打开编辑页面 | 点击“写博客” | 成功进入博客编辑页 |
| EDIT-002 | 输入博客标题 | 在标题框输入内容 | 标题输入成功 |
| EDIT-003 | 输入博客正文 | 在编辑器中输入正文 | 正文输入成功 |
| EDIT-004 | 发布空博客 | 不填写内容直接发布 | 系统阻止发布并提示错误 |
| EDIT-005 | 发布博客 | 填写标题和正文后点击发布 | 页面提示“博客发表成功” |
| EDIT-006 | 发布弹窗处理 | 获取浏览器 Alert 文本 | Alert 内容为“博客发表成功” |
| EDIT-007 | 发布后页面跳转 | 关闭成功弹窗 | 跳转到博客列表页 |
| EDIT-008 | 新博客标题验证 | 查找刚发布的博客标题 | 标题与输入内容一致 |
| EDIT-009 | 新博客正文验证 | 进入详情页检查正文 | 正文与输入内容一致 |
六、关键自动化实现
6.1 显式等待
项目中使用 Selenium 的显式等待来提高脚本稳定性。
示例:
wait = WebDriverWait(self.driver, 10)
title = wait.until(
EC.visibility_of_element_located(
(By.CSS_SELECTOR, "#title")
)
)
显式等待的优点:
- 等待元素真正出现;
- 避免页面加载较慢导致测试失败;
- 比固定
sleep更稳定; - 可以明确等待条件;
- 减少不必要的等待时间。
常用等待条件包括:
EC.presence_of_element_located()
EC.visibility_of_element_located()
EC.element_to_be_clickable()
EC.alert_is_present()
6.2 博客正文编辑器处理
博客编辑页面使用了 Markdown 编辑器,页面中存在隐藏的 textarea。虽然 Selenium 可以找到该元素,但它可能处于不可交互状态,直接执行:
content.send_keys("博客正文")
可能出现:
ElementNotInteractableException
项目中使用 JavaScript 对编辑器进行处理:
content_text = "这是自动化测试发布的博客正文内容"
self.driver.execute_script("""
const content = arguments[0];
const editor =
document.querySelector(".vditor-sv__editor textarea") ||
document.querySelector(".vditor-ir__editor") ||
document.querySelector("[contenteditable='true']") ||
document.querySelector("textarea");
if (!editor) {
throw new Error("没有找到博客正文编辑器");
}
editor.focus();
if (editor.tagName.toLowerCase() === "textarea") {
const setter = Object.getOwnPropertyDescriptor(
HTMLTextAreaElement.prototype,
"value"
).set;
setter.call(editor, content);
} else {
editor.innerText = content;
}
editor.dispatchEvent(new Event("input", {
bubbles: true
}));
editor.dispatchEvent(new Event("change", {
bubbles: true
}));
""", content_text)
该处理方式解决了以下问题:
- Selenium 无法直接操作隐藏文本框;
- 页面实际可编辑区域与 DOM 中的
textarea不一致; - 通过 JavaScript 修改编辑器内容;
- 触发
input和change事件,使前端应用能够感知内容变化。
这部分内容可以作为项目中的一个技术难点进行重点介绍。
6.3 浏览器 Alert 弹窗处理
点击发布后,页面会弹出:
博客发表成功
如果不处理弹窗,继续执行:
self.driver.current_url
会产生:
UnexpectedAlertPresentException
正确处理方式如下:
submit.click()
alert = WebDriverWait(
self.driver,
10
).until(
EC.alert_is_present()
)
alert_text = alert.text
print("发布提示:", alert_text)
assert alert_text == "博客发表成功"
alert.accept()
wait.until(
lambda driver: "blog_list.html" in driver.current_url
)
print("发布后当前页面地址:", self.driver.current_url)
这里包含了三个验证点:
- 等待弹窗出现;
- 验证弹窗内容;
- 点击确定关闭弹窗。
这种方式比单纯使用 time.sleep() 更可靠。
七、测试结果
7.1 当前测试结果
根据项目运行过程,目前已验证的场景包括:
| 模块 | 测试结果 | 说明 |
|---|---|---|
| 错误密码登录 | 通过 | 页面能够显示“密码不正确” |
| 正确账号登录 | 通过 | 成功进入博客列表页 |
| 博客列表页面 | 通过 | 页面地址和内容验证通过 |
| 博客详情页面 | 通过 | 详情页面能够正常打开 |
| 博客发布功能 | 功能成功 | 页面显示“博客发表成功” |
| 发布成功弹窗处理 | 已定位问题 | 需要在读取 URL 前关闭 Alert |
| 发布后标题验证 | 待最终确认 | 需要处理弹窗后继续执行断言 |
7.2 测试执行中的问题
问题一:正文编辑器无法直接输入
错误信息:
ElementNotInteractableException
原因:
页面中的 textarea 元素虽然存在,但处于隐藏或不可交互状态。真正的编辑区域由 Markdown 编辑器动态生成。
解决方案:
- 使用 JavaScript 获取可编辑区域;
- 修改编辑器内容;
- 手动触发
input和change事件; - 在测试代码中兼容
textarea和contenteditable元素。
问题二:发布成功后读取 URL 报错
错误信息:
UnexpectedAlertPresentException
原因:
点击发布按钮后,网页弹出浏览器原生 Alert:
博客发表成功
此时 Selenium 不能继续操作当前页面,必须先处理弹窗。
解决方案:
alert = WebDriverWait(
self.driver,
10
).until(
EC.alert_is_present()
)
assert alert.text == "博客发表成功"
alert.accept()
处理完成后,再获取页面地址并执行后续断言。
问题三:固定等待可能导致测试不稳定
早期代码中使用:
time.sleep(2)
这种方式虽然简单,但存在以下问题:
- 网络较慢时等待时间不足;
- 页面加载较快时浪费时间;
- 测试结果受机器性能影响;
- 无法准确判断页面是否真正加载完成。
改进方式:
wait.until(
EC.element_to_be_clickable(
(By.CSS_SELECTOR, "#submit")
)
)
或:
wait.until(
lambda driver: "blog_list.html" in driver.current_url
)
八、测试数据设计
8.1 登录测试数据
| 数据类型 | 用户名 | 密码 | 预期结果 |
|---|---|---|---|
| 正确数据 | 合法账号 | 正确密码 | 登录成功 |
| 错误密码 | 合法账号 | 错误密码 | 提示密码不正确 |
| 错误用户名 | 不存在账号 | 任意密码 | 登录失败 |
| 空用户名 | 空 | 正确密码 | 提示用户名不能为空 |
| 空密码 | 合法账号 | 空 | 提示密码不能为空 |
8.2 博客发布测试数据
| 字段 | 测试值 |
|---|---|
| 博客标题 | 自动化测试创建 |
| 博客正文 | 这是自动化测试发布的博客正文内容 |
| 发布方式 | 点击发布按钮 |
| 预期提示 | 博客发表成功 |
| 预期跳转 | blog_list.html |
| 预期结果 | 列表中出现新博客 |
九、项目中的测试方法
9.1 黑盒测试
从用户角度验证博客系统功能,不关注内部实现细节。
主要验证:
- 输入和输出是否符合预期;
- 页面是否正确跳转;
- 提示信息是否正确;
- 博客数据是否正确展示。
9.2 功能测试
针对博客系统核心功能编写测试用例,包括:
- 登录;
- 注销;
- 博客列表;
- 博客详情;
- 博客发布。
9.3 正向测试
使用正常数据验证系统功能是否能够正常完成。
例如:
- 正确账号登录;
- 正确填写标题和正文;
- 正常发布博客;
- 正常查看博客详情。
9.4 反向测试
使用异常数据验证系统的容错能力。
例如:
- 输入错误密码;
- 输入空用户名;
- 输入空密码;
- 发布空博客;
- 不处理弹窗时继续访问页面。
9.5 回归测试
每次修改测试脚本或页面后,重新执行已有测试用例,确认原有功能没有受到影响。
十、项目难点和解决方案
10.1 难点一:页面元素定位
页面中有多个结构相似的元素,部分元素没有明显的唯一 ID。
解决方案:
- 优先使用唯一 ID;
- 其次使用稳定的 CSS Selector;
- 避免过度依赖绝对 XPath;
- 对关键元素增加显式等待;
- 对页面结构进行分析后再确定定位表达式。
例如:
(By.CSS_SELECTOR, "#title")
比复杂的层级定位更加稳定。
10.2 难点二:动态页面加载
页面内容不是打开页面后立即出现,直接查找元素可能出现:
NoSuchElementException
解决方案:
wait.until(
EC.visibility_of_element_located(
(By.CSS_SELECTOR, "#title")
)
)
10.3 难点三:富文本或 Markdown 编辑器
编辑器表面上是一个可输入区域,但实际 DOM 结构可能包含:
- 隐藏的
textarea; - 可编辑的
div; - 预览区域;
- 动态生成的内部节点。
解决方案:
- 通过浏览器开发者工具分析 DOM;
- 查找
contenteditable属性; - 使用 JavaScript 处理隐藏控件;
- 触发前端需要的事件。
10.4 难点四:原生浏览器弹窗
发布成功后浏览器弹出 Alert,导致 Selenium 暂停页面操作。
解决方案:
alert = wait.until(EC.alert_is_present())
assert alert.text == "博客发表成功"
alert.accept()
10.5 难点五:测试流程之间存在依赖
博客列表测试、详情测试和发布测试需要先完成登录。
解决方案:
- 统一在测试入口中控制执行顺序;
- 将登录流程封装为公共方法;
- 在每个测试开始前确认当前页面状态;
- 后续可以改造成独立测试,避免用例之间相互依赖。
十一、项目质量改进方向
11.1 引入 Pytest
目前项目采用普通 Python 方法执行测试,可以进一步迁移到 Pytest。
例如:
import pytest
class TestBlog:
def test_login_success(self):
pass
def test_blog_list(self):
pass
def test_blog_publish(self):
pass
使用 Pytest 后可以获得:
- 自动发现测试用例;
- 更清晰的测试报告;
- Fixture 管理浏览器;
- 参数化测试;
- 失败重试;
- 标记测试用例;
- 与 CI 工具集成。
11.2 使用 Fixture 管理浏览器生命周期
示例:
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
这样可以统一管理:
- 浏览器启动;
- 浏览器关闭;
- 测试前置条件;
- 测试后清理工作。
11.3 引入 Page Object Model
可以将页面操作和测试断言分离。
示例目录:
pages
├─ login_page.py
├─ blog_list_page.py
├─ blog_detail_page.py
└─ blog_edit_page.py
tests
├─ test_login.py
├─ test_blog_list.py
├─ test_blog_detail.py
└─ test_blog_edit.py
页面对象示例:
class BlogEditPage:
def __init__(self, driver):
self.driver = driver
def input_title(self, title):
self.driver.find_element(
By.CSS_SELECTOR,
"#title"
).send_keys(title)
def click_submit(self):
self.driver.find_element(
By.CSS_SELECTOR,
"#submit"
).click()
这样可以降低页面结构变化对测试代码的影响。
11.4 增加日志记录
可以使用 Python 的 logging 模块:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.info("开始执行博客发布测试")
logging.info("博客标题输入完成")
logging.info("发布成功提示已确认")
日志可以帮助定位:
- 测试执行到哪个步骤;
- 哪一步出现异常;
- 当前页面地址;
- 当前使用的测试数据;
- 页面跳转是否成功。
11.5 自动生成测试报告
可以使用:
pytest --html=report.html
或者使用 Allure:
pytest --alluredir=allure-results
allure serve allure-results
测试报告中可以包含:
- 用例名称;
- 执行结果;
- 错误信息;
- 截图;
- 执行时间;
- 测试步骤;
- 环境信息。
11.6 接入持续集成
后续可以将项目接入 Jenkins 或 GitHub Actions,实现:
提交代码
↓
自动安装依赖
↓
启动测试环境
↓
执行自动化测试
↓
生成测试报告
↓
发送测试结果
十二、项目成果
通过本项目,完成了博客系统核心功能的自动化验证,主要成果如下:
- 实现了基于 Selenium 的浏览器自动化测试;
- 完成登录模块正向和反向测试;
- 完成博客列表页面测试;
- 完成博客详情页面测试;
- 完成博客发布流程测试;
- 实现页面元素定位和文本断言;
- 实现页面跳转地址验证;
- 实现浏览器 Alert 弹窗处理;
- 解决 Markdown 编辑器无法直接输入的问题;
- 使用显式等待提高测试稳定性;
- 实现测试截图保存;
- 根据测试异常定位页面交互和脚本问题;
- 对自动化测试过程中的异常进行了分析和修复;
- 具备从测试执行、问题定位到脚本改进的完整实践经验。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2401_82842337/article/details/166845274



