跳到主要内容用 Python+Selenium 给博客系统搭一套 UI 自动化测试框架 | 极客日志Python
用 Python+Selenium 给博客系统搭一套 UI 自动化测试框架
以C++后端的博客系统为被测对象,使用Python+Selenium搭建了UI自动化测试框架,涵盖登录、列表、详情、编辑四个核心模块的14条用例。通过封装驱动和截图工具、设计统一执行入口,实现了正常与异常场景的自动化验证,并输出了测试报告。整体方案实用,适合作为自动化测试入门参考。
给一个用 C++ 写的博客系统做 UI 自动化,我选了 Python + Selenium 这套老牌组合。下面把从环境搭建到报告输出的过程记录下来,包括踩过的坑和做过的取舍。
被测系统是一个 Web 端的博客,核心功能就四块:登录、博客列表、博客详情、博客编辑。需求很明确——把关键流程自动化,减少手动回归的时间,同时生成的报告要让团队看得懂。
环境准备
系统要求 Python 3.8+,Chrome 浏览器 110+。我习惯用 webdriver-manager 自动管理 ChromeDriver,省得去手动下载还总版本不匹配。
pip install selenium==4.10.0
pip install webdriver-manager==4.0.0
项目目录结构如下,清晰的分层让后期维护方便不少:
blog_auto_test/
├── common/
│ └── Utils.py
├── cases/
│ ├── BlogLogin.py
│ ├── BlogList.py
│ ├── BlogEdit.py
│ └── BlogDetail.py
├── images/
├── reports/
└── RunCases.py
公共模块:免得每个用例都写一遍
common/Utils.py 封装了两件事:浏览器驱动的创建/销毁,还有万能的截图。驱动做成全局单例,所有用例共享一个浏览器实例,别每跑一条用例就开个新窗口,慢还坑。截图按日期分文件夹,文件名带用例名和时间戳,出问题回头找截图方便。
import datetime
import os.path
import sys
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import WebDriverException
class Driver:
driver = None
def __init__(self):
try:
options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')
options.add_argument('--disable-gpu')
options.add_argument('window-size=1920,1080')
.driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
.driver.set_page_load_timeout()
.driver.implicitly_wait()
()
WebDriverException e:
()
Exception e:
()
():
:
dirname = datetime.datetime.now().strftime()
screenshot_dir =
os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)
case_name = sys._getframe().f_back.f_code.co_name
filename =
screenshot_path = os.path.join(screenshot_dir, filename)
.driver.save_screenshot(screenshot_path)
screenshot_path
Exception e:
()
():
.driver:
.driver.quit()
()
blog_driver = Driver()
self
self
10
self
5
print
f"驱动初始化成功!ChromeDriver 版本:{self.driver.capabilities['chrome']['chromedriverVersion']}"
except
as
print
f"驱动初始化失败:{str(e)}"
raise
except
as
print
f"未知错误:{str(e)}"
raise
def
get_screen_shot
self
try
'%Y-%m-%d'
f"../images/{dirname}"
if
not
f"{case_name}-{datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S')}.png"
self
return
except
as
print
f"截图失败:{str(e)}"
return
None
def
quit_driver
self
if
self
self
print
"🔌 驱动已关闭,资源释放完成"
隐式等待设了 5 秒,基本够用;如果页面偶尔抽风,显式等待更稳,以后优化时再换。
登录模块:一上来就得跑通
登录是所有操作的前提,写了正常登录和三种异常:密码错误、用户名为空、密码为空。流程都是清空输入框 -> 输入数据 -> 点击提交 -> 断言。为了模拟未登录跳转,异常用例里用 assert current_url 判断没跳走。
import time
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver
class BlogLogin:
def __init__(self):
self.login_url = "http://192.168.47.135:8653/blog_system/blog_login.html"
self.driver = blog_driver.driver
self.driver.get(self.login_url)
print(f"打开登录页面:{self.login_url}")
def clear_input(self):
username_input = self.driver.find_element(By.CSS_SELECTOR, "#username")
password_input = self.driver.find_element(By.CSS_SELECTOR, "#password")
username_input.clear()
password_input.clear()
print("清空账号密码输入框")
def login_suc_test(self, username="admin", password="123"):
try:
print(f"\n===== 执行正常登录测试(账号:{username},密码:{password})=====")
self.clear_input()
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys(username)
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(password)
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
time.sleep(2)
avatar_element = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > img")
assert avatar_element.is_displayed()
print(f"正常登录测试通过!{username}账号登录成功")
blog_driver.get_screen_shot()
self.driver.back()
time.sleep(1)
except NoSuchElementException:
print(f"正常登录测试失败:未找到博主头像元素")
blog_driver.get_screen_shot()
raise
except AssertionError:
print(f"正常登录测试失败:博主头像元素不可见")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"正常登录测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def login_fail_test(self, username="admin", password="111", expect_msg="用户名或密码错误!"):
try:
print(f"\n===== 执行异常登录测试(账号:{username},密码:{password})=====")
self.clear_input()
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys(username)
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(password)
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
time.sleep(1)
actual_msg = self.driver.find_element(By.CSS_SELECTOR, "body").text
assert expect_msg in actual_msg, f"错误提示不匹配!预期:{expect_msg},实际:{actual_msg}"
print(f"异常登录测试通过!错误提示符合预期")
blog_driver.get_screen_shot()
self.driver.back()
time.sleep(1)
except NoSuchElementException:
print(f"异常登录测试失败:未找到错误提示元素")
blog_driver.get_screen_shot()
raise
except AssertionError as e:
print(f"异常登录测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"异常登录测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def login_empty_username_test(self):
print(f"\n===== 执行账号为空登录测试 =====")
self.clear_input()
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123")
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
time.sleep(1)
assert self.driver.current_url == self.login_url, "账号为空时页面不应跳转"
print(f"账号为空登录测试通过!页面未跳转")
blog_driver.get_screen_shot()
def login_empty_password_test(self):
print(f"\n===== 执行密码为空登录测试 =====")
self.clear_input()
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("admin")
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
time.sleep(1)
assert self.driver.current_url == self.login_url, "密码为空时页面不应跳转"
print(f"密码为空登录测试通过!页面未跳转")
blog_driver.get_screen_shot()
if __name__ == "__main__":
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
login_test.login_fail_test("admin", "111", "用户名或密码错误!")
login_test.login_empty_username_test()
login_test.login_empty_password_test()
blog_driver.quit_driver()
注意一点:登录成功后我加了 driver.back(),回到登录页再跑下一个异常用例,不然有些系统登录态下再访问登录页会直接跳走。如果被测系统没这逻辑,可以省掉。
博客列表模块
这里两个场景:登录后正常访问,断言头像可见、博客数量≥11(系统里至少有十几篇),点击第一篇能跳转到详情页;未登录时访问会直接重定向到登录页。未登录测试需要完全关闭前一个驱动,重建一个干净的会话。
import time
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver
from cases.BlogLogin import BlogLogin
class BlogList:
def __init__(self):
self.list_url = "http://192.168.47.135:8653/blog_system/blog_list.html"
self.driver = blog_driver.driver
print(f"博客列表页 URL:{self.list_url}")
def list_login_status_test(self):
try:
print(f"\n===== 执行登录状态下博客列表测试 =====")
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
self.driver.get(self.list_url)
time.sleep(2)
avatar_element = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > img")
assert avatar_element.is_displayed(), "博主头像未显示,登录状态可能失效"
first_blog = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > a")
assert first_blog.is_displayed(), "第一篇博客链接未找到"
all_blogs = self.driver.find_elements(By.CSS_SELECTOR, "body > div.container > div.right > div")
blog_count = len(all_blogs)
assert blog_count > 10, f"博客数量不足 10 篇,实际数量:{blog_count}"
first_blog.click()
time.sleep(2)
assert self.driver.title == "博客详情页", f"跳转失败,当前页面标题:{self.driver.title}"
blog_driver.get_screen_shot()
print("===== 登录状态下博客列表测试通过 =====")
except NoSuchElementException as e:
print(f"登录状态下博客列表测试失败:未找到元素 - {str(e)}")
blog_driver.get_screen_shot()
raise
except AssertionError as e:
print(f"登录状态下博客列表测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"登录状态下博客列表测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def list_unlogin_status_test(self):
try:
print(f"\n===== 执行未登录状态下博客列表测试 =====")
blog_driver.quit_driver()
new_driver = Driver()
self.driver = new_driver.driver
self.driver.get(self.list_url)
time.sleep(2)
assert self.driver.current_url == "http://192.168.47.135:8653/blog_system/blog_login.html",
print("断言通过:未登录状态下访问列表页,自动跳转至登录页")
new_driver.get_screen_shot()
new_driver.quit_driver()
print("===== 未登录状态下博客列表测试通过 =====")
except AssertionError as e:
print(f"未登录状态下博客列表测试失败:{str(e)}")
new_driver.get_screen_shot()
raise
except Exception as e:
print(f"未登录状态下博客列表测试异常:{str(e)}")
new_driver.get_screen_shot()
raise
if __name__ == "__main__":
list_test = BlogList()
list_test.list_login_status_test()
list_test.list_unlogin_status_test()
博客详情模块
详情页依赖列表页跳转,所以登录后先去列表页点第一篇,再断言标题、日期、正文元素都显示。未登录就直接访问带 blogId 的 URL,同样应该被重定向。
import time
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver
from cases.BlogLogin import BlogLogin
from cases.BlogList import BlogList
class BlogDetail:
def __init__(self, blog_id=15):
self.detail_url = f"http://192.168.47.135:8653/blog_system/blog_detail.html?blogId={blog_id}"
self.driver = blog_driver.driver
self.blog_id = blog_id
print(f"博客详情页 URL(ID:{blog_id}):{self.detail_url}")
def detail_login_status_test(self):
try:
print(f"\n===== 执行登录状态下博客详情测试(ID:{self.blog_id})=====")
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
list_test = BlogList()
list_test.driver.get(list_test.list_url)
time.sleep(2)
first_blog = list_test.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > a")
first_blog.click()
time.sleep(2)
assert self.driver.title == "博客详情页", f"当前页面不是详情页,标题:{self.driver.title}"
blog_title = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div > h3")
assert blog_title.is_displayed(), "博客标题未显示"
publish_date = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div > div.date")
assert publish_date.is_displayed(), "发布日期未显示"
blog_content = self.driver.find_element(By.CSS_SELECTOR, "#content")
assert blog_content.is_displayed(), "博客正文未显示"
blog_driver.get_screen_shot()
print("===== 登录状态下博客详情测试通过 =====")
except NoSuchElementException as e:
print(f"登录状态下博客详情测试失败:未找到元素 - {str(e)}")
blog_driver.get_screen_shot()
raise
except AssertionError as e:
print(f"登录状态下博客详情测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"登录状态下博客详情测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def detail_unlogin_status_test(self):
try:
print(f"\n===== 执行未登录状态下博客详情测试(ID:{self.blog_id})=====")
blog_driver.quit_driver()
new_driver = Driver()
self.driver = new_driver.driver
self.driver.get(self.detail_url)
time.sleep(2)
assert self.driver.current_url == "http://192.168.47.135:8653/blog_system/blog_login.html",
print("断言通过:未登录状态下访问详情页,自动跳转至登录页")
new_driver.get_screen_shot()
new_driver.quit_driver()
print("===== 未登录状态下博客详情测试通过 =====")
except AssertionError as e:
print(f"未登录状态下博客详情测试失败:{str(e)}")
new_driver.get_screen_shot()
raise
except Exception as e:
print(f"未登录状态下博客详情测试异常:{str(e)}")
new_driver.get_screen_shot()
raise
if __name__ == "__main__":
detail_test = BlogDetail(blog_id=15)
detail_test.detail_login_status_test()
detail_test.detail_unlogin_status_test()
博客编辑模块
编辑模块的用例稍多:正常提交、标题为空提交、内容为空提交、未登录提交。正常提交后要去列表页确认新博客出现。标题用了时间戳防止重复。异常情况校验页面没跳转,说明前端拦住了。
import time
import datetime
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver
from cases.BlogLogin import BlogLogin
class BlogEdit:
def __init__(self):
self.edit_url = "http://192.168.47.135:8653/blog_system/blog_edit.html"
self.driver = blog_driver.driver
print(f"博客编辑页 URL:{self.edit_url}")
def edit_login_normal_submit_test(self):
try:
print(f"\n===== 执行登录状态下正常提交博客测试 =====")
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
self.driver.get(self.edit_url)
time.sleep(2)
title_input = self.driver.find_element(By.CSS_SELECTOR, "#title")
content_input = self.driver.find_element(By.CSS_SELECTOR, "#content")
submit_btn = self.driver.find_element(By.CSS_SELECTOR, "#submit")
blog_title = f"自动化测试实战_测试博客_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}"
blog_content = "这是一篇通过自动化测试脚本提交的博客,用于验证编辑模块的正常提交功能。"
title_input.clear()
title_input.send_keys(blog_title)
content_input.clear()
content_input.send_keys(blog_content)
submit_btn.click()
time.sleep(3)
assert self.driver.title == "博客列表页", f"提交后未跳转至列表页,当前标题:{self.driver.title}"
all_blogs = self.driver.find_elements(By.CSS_SELECTOR, "body > div.container > div.right > div > a")
blog_titles = [blog.text for blog in all_blogs]
assert blog_title in blog_titles, f"新增博客未在列表中显示(标题:{blog_title})"
blog_driver.get_screen_shot()
print("===== 登录状态下正常提交博客测试通过 =====")
except NoSuchElementException as e:
print(f"正常提交博客测试失败:未找到元素 - {str(e)}")
blog_driver.get_screen_shot()
raise
except AssertionError as e:
print(f"正常提交博客测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"正常提交博客测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def edit_login_no_title_submit_test(self):
try:
print(f"\n===== 执行登录状态下无标题提交博客测试 =====")
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
self.driver.get(self.edit_url)
time.sleep(2)
content_input = self.driver.find_element(By.CSS_SELECTOR, "#content")
submit_btn = self.driver.find_element(By.CSS_SELECTOR, "#submit")
blog_content = "这是无标题提交测试的内容,预期提交失败。"
content_input.clear()
content_input.send_keys(blog_content)
submit_btn.click()
time.sleep(1)
assert self.driver.current_url == self.edit_url, f"无标题提交时页面不应跳转,当前 URL:{self.driver.current_url}"
blog_driver.get_screen_shot()
print("===== 登录状态下无标题提交博客测试通过 =====")
except AssertionError as e:
print(f"无标题提交博客测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"无标题提交博客测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def edit_login_no_content_submit_test(self):
try:
print(f"\n===== 执行登录状态下无内容提交博客测试 =====")
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
self.driver.get(self.edit_url)
time.sleep(2)
title_input = self.driver.find_element(By.CSS_SELECTOR, "#title")
submit_btn = self.driver.find_element(By.CSS_SELECTOR, "#submit")
blog_title = "无内容提交测试的标题"
title_input.clear()
title_input.send_keys(blog_title)
submit_btn.click()
time.sleep(1)
assert self.driver.current_url == self.edit_url, f"无内容提交时页面不应跳转,当前 URL:{self.driver.current_url}"
blog_driver.get_screen_shot()
print("===== 登录状态下无内容提交博客测试通过 =====")
except AssertionError as e:
print(f"无内容提交博客测试失败:{str(e)}")
blog_driver.get_screen_shot()
raise
except Exception as e:
print(f"无内容提交博客测试异常:{str(e)}")
blog_driver.get_screen_shot()
raise
def edit_unlogin_submit_test(self):
try:
print(f"\n===== 执行未登录状态下提交博客测试 =====")
blog_driver.quit_driver()
new_driver = Driver()
self.driver = new_driver.driver
self.driver.get(self.edit_url)
time.sleep(2)
title_input = self.driver.find_element(By.CSS_SELECTOR, "#title")
content_input = self.driver.find_element(By.CSS_SELECTOR, "#content")
submit_btn = self.driver.find_element(By.CSS_SELECTOR, "#submit")
title_input.send_keys("未登录提交测试")
content_input.send_keys("未登录状态下不应提交成功")
submit_btn.click()
time.sleep(2)
assert self.driver.current_url == "http://192.168.47.135:8653/blog_system/blog_login.html",
print("断言通过:未登录状态下提交,跳转至登录页")
new_driver.get_screen_shot()
new_driver.quit_driver()
print("===== 未登录状态下提交博客测试通过 =====")
except AssertionError as e:
print(f"未登录提交博客测试失败:{str(e)}")
new_driver.get_screen_shot()
raise
except Exception as e:
print(f"未登录提交博客测试异常:{str(e)}")
new_driver.get_screen_shot()
raise
if __name__ == "__main__":
edit_test = BlogEdit()
edit_test.edit_login_normal_submit_test()
edit_test.edit_login_no_title_submit_test()
edit_test.edit_login_no_content_submit_test()
edit_test.edit_unlogin_submit_test()
执行入口:一把嗦跑完
RunCases.py 负责按顺序把所有用例过一遍,最后统计通过率。异常会中断整个流程并标记失败,实际项目里可以配上 pytest 做更细粒度的用例管理和重试,但简版先这样。
import sys
import time
from common.Utils import blog_driver
from cases.BlogLogin import BlogLogin
from cases.BlogList import BlogList
from cases.BlogDetail import BlogDetail
from cases.BlogEdit import BlogEdit
def run_all_cases():
print("="*50)
print("开始执行博客系统自动化测试用例")
print(f"执行时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")
print("="*50)
test_results = {"passed": 0, "failed": 0, "total": 0}
try:
print("\n" + "="*30)
print("开始执行登录模块测试")
print("="*30)
login_test = BlogLogin()
login_test.login_suc_test("admin", "123")
test_results["passed"] += 1
login_test.login_suc_test("lisi", "123")
test_results["passed"] += 1
login_test.login_fail_test("admin", "111", "用户名或密码错误!")
test_results["passed"] += 1
login_test.login_empty_username_test()
test_results["passed"] += 1
login_test.login_empty_password_test()
test_results["passed"] += 1
print("\n" + "="*30)
print("开始执行博客列表模块测试")
print("="*30)
list_test = BlogList()
list_test.list_login_status_test()
test_results["passed"] += 1
list_test.list_unlogin_status_test()
test_results["passed"] += 1
print("\n" + "="*30)
print("开始执行博客详情模块测试")
print("="*30)
detail_test = BlogDetail(blog_id=15)
detail_test.detail_login_status_test()
test_results["passed"] += 1
detail_test.detail_unlogin_status_test()
test_results["passed"] += 1
print("\n" + "="*30)
print("开始执行博客编辑模块测试")
print("="*30)
edit_test = BlogEdit()
edit_test.edit_login_normal_submit_test()
test_results["passed"] += 1
edit_test.edit_login_no_title_submit_test()
test_results["passed"] += 1
edit_test.edit_login_no_content_submit_test()
test_results["passed"] += 1
edit_test.edit_unlogin_submit_test()
test_results["passed"] += 1
except Exception as e:
print(f"\n测试执行过程中出现异常:{str(e)}")
test_results["failed"] += 1
finally:
test_results["total"] = test_results["passed"] + test_results["failed"]
blog_driver.quit_driver()
print("\n" + "="*50)
print("博客系统自动化测试执行完成")
print("="*50)
print(f"总用例数:{test_results['total']}")
print(f"通过用例数:{test_results['passed']}")
print(f"失败用例数:{test_results['failed']}")
pass_rate = (test_results['passed'] / test_results['total']) * 100 if test_results['total'] > 0 else 100
print(f"测试通过率:{pass_rate:.2f}%")
print("="*50)
if test_results["failed"] > 0:
sys.exit(1)
else:
sys.exit(0)
if __name__ == "__main__":
run_all_cases()
测试报告:给老大看的
跑完总要有个交代。下面这个模板可以直接用,把截图路径、实际数据填进去就行。14 条用例全部通过,没发现新缺陷,历史修复也没回归,上线决策的依据就齐了。
【测试报告】博客系统 UI 自动化测试报告
1. 报告基本信息
| 项目名称 | 博客系统(C++ 后端 Web 项目) | 版本号 | V1.0 |
|---|
| 发布类型 | 分级发布 | 测试负责人 | 测试负责人 |
| 测试完成日期 | 2026-1-14 | 联系方式 | (根据实际填写) |
| 评审人 | 评审人 | 批准人 | 批准人 |
| 评审日期 | 2026-1-14 | 批准日期 | 2026-1-14 |
2. 项目背景
2.1 测试目标及测试任务概括
- 测试目标:验证博客系统核心功能(登录、列表、详情、编辑)的正确性和稳定性,实现关键流程自动化,减少手动测试成本,支撑项目上线。
- 测试任务:设计并执行 14 条 UI 自动化测试用例,覆盖正常场景与异常场景,生成测试报告并跟踪问题修复。
2.2 被测系统及相关信息
- 被测系统:博客系统(Web 端);
- 系统地址:
http://192.168.47.135:8653/blog_system/;
- 依赖环境:Chrome 浏览器 110+、Python 3.8+、Selenium 4.10.0。
2.3 产品需求和设计文档
- 《博客系统需求文档》;
- 《博客系统技术设计文档》;
- 《博客系统 UI 设计图》。
3. 测试安排
| 模块 | 子模块 | 前端开发 | 后端开发 | 提测时间 | 测试负责人 | 工时 | 排期 | 进度 | 备注 |
|---|
| 登录模块 | 登录功能 | 开发人员 | 开发人员 | 2024-05-18 | 测试负责人 | 0.5d | 2026-1-10 | 测试完成 | 覆盖正常 / 异常登录 |
| 博客列表模块 | 列表展示、跳转 | 开发人员 | 开发人员 | 2024-05-18 | 测试负责人 | 0.5d | 2026-1-10 | 测试完成 | 登录 / 未登录状态 |
| 博客详情模块 | 详情展示 | 开发人员 | 开发人员 | 2024-05-18 | 测试负责人 | 0.5d | 2026-1-11 | 测试完成 | 登录 / 未登录状态 |
| 博客编辑模块 | 编辑、提交 | 开发人员 | 开发人员 | 2024-05-18 | 测试负责人 | 0.5d | 2026-1-11 | 测试完成 | 正常 / 异常提交 |
4. 测试结果汇总
4.1 测试用例执行情况
| 模块 | 用例总数 | 通过数 | 失败数 | 通过率 |
|---|
| 登录模块 | 5 | 5 | 0 | 100.00% |
| 博客列表模块 | 2 | 2 | 0 | 100.00% |
| 博客详情模块 | 2 | 2 | 0 | 100.00% |
| 博客编辑模块 | 5 | 5 | 0 | 100.00% |
| 总计 | 14 | 14 | 0 | 100.00% |
4.2 缺陷统计与分析
本次自动化测试未发现新增缺陷;历史缺陷修复验证:所有已提交的缺陷均已修复,且在本次自动化测试中无回归。
4.3 截图示例
| 测试场景 | 截图路径 | 说明 |
|---|
| admin 账号正常登录成功 | images/2026-1-10/loginSucTest-20260110143025.png | 跳转至列表页,显示博主头像 |
| 密码错误异常登录 | images/2026-1-10/loginFailTest-20260110143210.png | 显示 "用户名或密码错误!" |
| 博客列表页正常展示 | images/2026-1-11/listLoginStatusTest-20260111143508.png | 博客数量大于 10 篇 |
| 编辑博客正常提交 | images/2026-1-11/editLoginNormalSubmitTest-20260111144015.png | 新增博客显示在列表页 |
5. 测试结论与建议
5.1 测试结论
博客系统核心功能(登录、列表、详情、编辑)的 UI 自动化测试已全部完成,14 条用例全部通过,通过率 100%;系统在测试场景下运行稳定,未出现功能异常、崩溃等问题;自动化测试框架运行正常,能够准确执行测试用例、捕获异常并生成截图,可支撑后续迭代的回归测试。
5.2 后续建议
- 扩展用例覆盖范围:新增博客删除、修改密码、评论等功能的自动化用例;
- 集成 CI/CD 流程:将自动化测试脚本集成到 Jenkins 等工具,实现每次代码提交后自动执行测试;
- 优化框架稳定性:增加重试机制(如元素未找到时自动重试)、日志详细程度优化;
- 定期维护用例:当系统 UI 发生变更时,及时更新用例中的元素选择器,确保自动化用例的有效性。
整个框架搭出来不算复杂,但核心流程都能跑通。后续慢慢加数据驱动、日志、重试,再往 CI 里一挂,回归测试就能放心交给它了。
相关免费在线工具
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online