跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python大前端

Web 项目 UI 自动化测试实战:从零搭建博客系统测试框架

基于 Python 和 Selenium 构建博客系统 UI 自动化测试框架,涵盖环境配置、工具封装、用例开发与报告生成。通过驱动管理与断言验证,实现登录、列表、详情、编辑等核心流程的自动化回归,有效提升测试效率与质量,减少人工成本。

1739658202发布于 2026/3/26更新于 2026/9/1063 浏览
Web 项目 UI 自动化测试实战:从零搭建博客系统测试框架

Web 项目 UI 自动化测试实战:从零搭建博客系统测试框架

文章配图

前言

在软件开发流程中,重复的手动测试不仅耗时耗力,还容易出现遗漏。尤其是对于 Web 项目开发,每次迭代都要反复验证登录、列表展示等核心流程,稍有疏忽就可能让 bug 流入生产环境。自动化测试能自动执行预设用例、精准捕获异常并生成报告,让开发者把更多精力放在核心功能上。

本文将以经典的博客系统为案例,手把手教你用 Python 搭建一套完整的 UI 自动化测试框架,从用例设计到脚本开发,再到测试报告落地,全程实战、代码可直接复用。

一、项目背景与测试规划

1.1 项目介绍

本次实战的被测对象是一个基于 Web 的博客系统(后端 C++ 开发),核心功能模块包括:登录模块、博主信息模块、博客列表模块、博客编辑模块、博客详情模块。系统支持用户登录后查看、编辑、提交博客,未登录用户访问受限功能时会触发登录弹窗提示。

文章配图

1.2 测试目标

验证博客系统核心功能的稳定性和正确性;实现关键流程的自动化执行,减少手动测试成本;快速定位迭代过程中引入的回归 bug;输出清晰、规范的测试报告,支撑项目上线决策。

1.3 测试范围与用例设计

自动化测试并非 "面面俱到",而是聚焦核心流程和高频场景。结合系统功能,我们设计了以下测试用例(覆盖正常场景与异常场景):

文章配图

二、环境搭建:3 步搞定自动化测试前置准备

在开始编码前,我们需要先完成环境配置。请确保你的电脑已安装 Python 环境(3.8 及以上版本),然后按照以下步骤操作:

2.1 安装核心依赖包

打开命令行终端,执行以下命令安装所需库:

# 安装 Selenium(UI 自动化核心库)
pip install selenium==4.10.0
# 安装 webdriver-manager(自动管理浏览器驱动)
pip install webdriver-manager==4.0.0

2.2 浏览器配置

本文以 Chrome 浏览器为例(推荐版本 110+),无需手动下载 ChromeDriver:webdriver-manager 会自动检测浏览器版本并下载对应驱动,彻底解决 "驱动版本不匹配" 的问题。

如果需要使用 Firefox 或 Edge 浏览器,只需修改后续代码中的浏览器配置。

2.3 项目目录结构设计

一个清晰的目录结构是自动化框架可维护的关键。我们采用以下目录结构组织项目:

blog_auto_test/
├── common/           # 公共工具模块
│   └── Utils.py      # 驱动管理、截图等公共功能
├── cases/            # 测试用例模块
│   ├── BlogLogin.py  # 登录模块测试用例
│   ├── BlogList.py   # 博客列表模块测试用例
│   ├── BlogEdit.py   # 博客编辑模块测试用例
│   └── BlogDetail.py # 博客详情模块测试用例
├── images/           # 截图存储目录
├── reports/          # 测试报告目录
└── RunCases.py       # 测试用例执行入口

三、核心模块开发:封装公共工具,提高代码复用性

在编写具体测试用例前,我们先封装公共工具类 —— 这能避免重复编码,让后续用例开发更高效。核心公共功能包括:驱动对象创建、自动截图、异常处理等。

3.1 驱动管理与截图工具封装(common/Utils.py)

这里我们封装了一个 Driver 类来管理浏览器生命周期。注意,为了代码能运行,需要确保导入了 datetime 模块。

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):
        """初始化驱动对象,配置 Chrome 浏览器选项"""
        try:
            options = webdriver.ChromeOptions()
            # 可选配置:无头模式(适合服务器环境)
            # options.add_argument('--headless=new')
            # 忽略证书错误
            options.add_argument('--ignore-certificate-errors')
            # 禁用 GPU 加速
            options.add_argument('--disable-gpu')
            # 设置窗口大小
            options.add_argument('window-size=1920,1080')

            self.driver = webdriver.Chrome(
                service=Service(ChromeDriverManager().install()),
                options=options
            )
            self.driver.set_page_load_timeout(10)
            self.driver.implicitly_wait(5)
            print(f"驱动初始化成功!ChromeDriver 版本:{self.driver.capabilities['chrome']['chromedriverVersion']}")
        except WebDriverException as e:
            print(f"驱动初始化失败:{str(e)}")
            raise
        except Exception as e:
            print(f"未知错误:{str(e)}")
            raise

    def get_screen_shot(self):
        """截图功能:按日期创建文件夹,截图文件名包含用例名和时间戳"""
        try:
            dirname = datetime.datetime.now().strftime('%Y-%m-%d')
            screenshot_dir = f"../images/{dirname}"
            if not os.path.exists(screenshot_dir):
                os.makedirs(screenshot_dir)
            print(f"创建截图文件夹:{screenshot_dir}")

            case_name = sys._getframe().f_back.f_code.co_name
            filename = f"{case_name}-{datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S')}.png"
            screenshot_path = os.path.join(screenshot_dir, filename)
            self.driver.save_screenshot(screenshot_path)
            print(f"截图成功!路径:{screenshot_path}")
            return screenshot_path
        except Exception as e:
            print(f"截图失败:{str(e)}")
            return None

    def quit_driver(self):
        """关闭驱动,释放资源"""
        if self.driver:
            self.driver.quit()
            print("🔌 驱动已关闭,资源释放完成")

# 创建全局驱动实例(所有用例共享同一个驱动)
blog_driver = Driver()

3.2 代码说明与优化点

  • 驱动全局共享:通过类属性和全局实例,确保所有测试用例使用同一个浏览器实例,避免重复打开浏览器,提高执行效率。
  • 异常处理:捕获常见异常,并打印详细日志,方便问题定位。
  • 灵活配置:预留了无头模式、窗口大小等可选配置,可根据实际测试环境调整。
  • 截图优化:按日期分类存储截图,文件名包含用例名和时间戳,便于追溯测试场景。

四、测试用例开发:逐个模块实现自动化测试

接下来,我们按照测试规划,逐个模块编写测试用例。每个用例都遵循 "前置条件→操作步骤→断言验证" 的逻辑,确保测试结果的准确性。

4.1 登录模块测试(cases/BlogLogin.py)

登录模块是系统的入口,也是自动化测试的核心场景。我们需要覆盖正常登录和多种异常登录场景:

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)
            print(f"输入账号:{username},密码:{password}")
            self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
            print("点击登录按钮")
            time.sleep(2)
            try:
                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
            print(f"预期错误提示:{expect_msg}")
            print(f"实际错误提示:{actual_msg}")
            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 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_suc_test("lisi", "123")
    login_test.login_fail_test("admin", "111", "用户名或密码错误!")
    login_test.login_empty_username_test()
    login_test.login_empty_password_test()
    blog_driver.quit_driver()

4.2 博客列表模块测试(cases/BlogList.py)

博客列表模块的核心测试点是 "登录状态下正常访问" 和 "未登录状态下跳转登录页",同时需要验证博客数量和跳转功能:

import time
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver, 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)
            print(f"已登录,访问列表页:{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(), "博主头像未显示,登录状态可能失效"
            print("断言通过:博主头像正常显示")
            first_blog = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > a")
            assert first_blog.is_displayed(), "第一篇博客链接未找到"
            print(f"断言通过:第一篇博客链接正常显示(标题:{first_blog.text})")
            all_blogs = self.driver.find_elements(By.CSS_SELECTOR, "body > div.container > div.right > div")
            blog_count = len(all_blogs)
            print(f"当前博客总数:{blog_count}")
            assert blog_count > 10, f"博客数量不足 10 篇,实际数量:{blog_count}"
            print("断言通过:博客数量大于 10 篇")
            first_blog.click()
            time.sleep(2)
            assert self.driver.title == "博客详情页", f"跳转失败,当前页面标题:{self.driver.title}"
            print("断言通过:成功跳转至博客详情页")
            blog_driver.get_screen_shot()
            print("===== 登录状态下博客列表测试通过 =====")
        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)
            print(f"未登录,直接访问列表页:{self.list_url}")
            time.sleep(2)
            assert self.driver.current_url == "http://192.168.47.135:8653/blog_system/blog_login.html", \
                f"未跳转至登录页,当前 URL:{self.driver.current_url}"
            print("断言通过:未登录状态下访问列表页,自动跳转至登录页")
            new_driver.get_screen_shot()
            new_driver.quit_driver()
            print("===== 未登录状态下博客列表测试通过 =====")
        except Exception as e:
            print(f"未登录状态下博客列表测试异常:{str(e)}")
            raise

if __name__ == "__main__":
    list_test = BlogList()
    list_test.list_login_status_test()
    list_test.list_unlogin_status_test()

4.3 博客详情模块测试(cases/BlogDetail.py)

博客详情模块依赖博客列表模块的跳转,核心测试点是页面元素完整性和未登录访问限制:

import time
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver, 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}"
            print("断言通过:页面标题为'博客详情页'")
            blog_title = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div > h3")
            assert blog_title.is_displayed(), "博客标题未显示"
            print(f"断言通过:博客标题正常显示(标题:{blog_title.text})")
            publish_date = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div > div.date")
            assert publish_date.is_displayed(), "发布日期未显示"
            print(f"断言通过:发布日期正常显示(日期:{publish_date.text})")
            blog_content = self.driver.find_element(By.CSS_SELECTOR, "#content")
            assert blog_content.is_displayed(), "博客正文未显示"
            print(f"断言通过:博客正文正常显示(前 50 字:{blog_content.text[:50]}...)")
            blog_driver.get_screen_shot()
            print("===== 登录状态下博客详情测试通过 =====")
        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)
            print(f"未登录,直接访问详情页:{self.detail_url}")
            time.sleep(2)
            assert self.driver.current_url == "http://192.168.47.135:8653/blog_system/blog_login.html", \
                f"未跳转至登录页,当前 URL:{self.driver.current_url}"
            print("断言通过:未登录状态下访问详情页,自动跳转至登录页")
            new_driver.get_screen_shot()
            new_driver.quit_driver()
            print("===== 未登录状态下博客详情测试通过 =====")
        except Exception as e:
            print(f"未登录状态下博客详情测试异常:{str(e)}")
            raise

if __name__ == "__main__":
    detail_test = BlogDetail(blog_id=15)
    detail_test.detail_login_status_test()
    detail_test.detail_unlogin_status_test()

4.4 博客编辑模块测试(cases/BlogEdit.py)

博客编辑模块是核心功能之一,需要覆盖正常提交和异常提交(无标题、无内容)场景:

import time
import datetime
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from common.Utils import blog_driver, 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)
            print(f"输入博客标题:{blog_title}")
            submit_btn.click()
            time.sleep(3)
            assert self.driver.title == "博客列表页", f"提交后未跳转至列表页,当前标题:{self.driver.title}"
            print("断言通过:提交后成功跳转至列表页")
            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})"
            print(f"断言通过:新增博客已显示在列表页")
            blog_driver.get_screen_shot()
            print("===== 登录状态下正常提交博客测试通过 =====")
        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}"
            print("断言通过:无标题提交时页面未跳转")
            blog_driver.get_screen_shot()
            print("===== 登录状态下无标题提交博客测试通过 =====")
        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}"
            print("断言通过:无内容提交时页面未跳转")
            blog_driver.get_screen_shot()
            print("===== 登录状态下无内容提交博客测试通过 =====")
        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", \
                f"未登录提交应跳转至登录页,当前 URL:{self.driver.current_url}"
            print("断言通过:未登录状态下提交,跳转至登录页")
            new_driver.get_screen_shot()
            new_driver.quit_driver()
            print("===== 未登录状态下提交博客测试通过 =====")
        except Exception as e:
            print(f"未登录提交博客测试异常:{str(e)}")
            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)

为了方便执行所有测试用例,我们创建一个统一的执行入口文件,按模块顺序执行测试:

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:
        # 1. 执行登录模块测试
        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

        # 2. 执行博客列表模块测试
        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

        # 3. 执行博客详情模块测试
        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

        # 4. 执行博客编辑模块测试
        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()

六、测试报告生成:打造专业、清晰的测试成果文档

测试执行完成后,需要生成一份规范的测试报告,用于向团队展示测试结果、进度和问题。以下为大家提供基于本次实战的测试报告模板(可根据实际项目调整):

【测试报告】博客系统 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.5d2026-1-10测试完成覆盖正常 / 异常登录
博客列表模块列表展示、跳转前端开发后端开发2024-05-18测试负责人0.5d2026-1-10测试完成登录 / 未登录状态
博客详情模块详情展示前端开发后端开发2024-05-18测试负责人0.5d2026-1-11测试完成登录 / 未登录状态
博客编辑模块编辑、提交前端开发后端开发2024-05-18测试负责人0.5d2026-1-11测试完成正常 / 异常提交
4. 测试结果汇总
4.1 测试用例执行情况
模块用例总数通过数失败数通过率
登录模块550100.00%
博客列表模块220100.00%
博客详情模块220100.00%
博客编辑模块550100.00%
总计14140100.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 发生变更时,及时更新用例中的元素选择器,确保自动化用例的有效性。

总结

通过本次博客系统的自动化测试实战,我们从 0 到 1 搭建了一套完整的 UI 自动化测试框架,覆盖了用例设计、脚本开发、执行入口、报告生成等全流程,并且实现了代码复用、异常处理、截图追溯等关键功能。

自动化测试是一个 "实践出真知" 的领域,只有多动手、多踩坑,才能不断提升自己的技术水平。希望本文的实战案例能为你提供一些帮助。

目录

  1. Web 项目 UI 自动化测试实战:从零搭建博客系统测试框架
  2. 前言
  3. 一、项目背景与测试规划
  4. 1.1 项目介绍
  5. 1.2 测试目标
  6. 1.3 测试范围与用例设计
  7. 二、环境搭建:3 步搞定自动化测试前置准备
  8. 2.1 安装核心依赖包
  9. 安装 Selenium(UI 自动化核心库)
  10. 安装 webdriver-manager(自动管理浏览器驱动)
  11. 2.2 浏览器配置
  12. 2.3 项目目录结构设计
  13. 三、核心模块开发:封装公共工具,提高代码复用性
  14. 3.1 驱动管理与截图工具封装(common/Utils.py)
  15. 创建全局驱动实例(所有用例共享同一个驱动)
  16. 3.2 代码说明与优化点
  17. 四、测试用例开发:逐个模块实现自动化测试
  18. 4.1 登录模块测试(cases/BlogLogin.py)
  19. 4.2 博客列表模块测试(cases/BlogList.py)
  20. 4.3 博客详情模块测试(cases/BlogDetail.py)
  21. 4.4 博客编辑模块测试(cases/BlogEdit.py)
  22. 五、测试用例执行入口:一键运行所有测试(RunCases.py)
  23. 六、测试报告生成:打造专业、清晰的测试成果文档
  24. 【测试报告】博客系统 UI 自动化测试报告
  25. 1. 报告基本信息
  26. 2. 项目背景
  27. 2.1 测试目标及测试任务概括
  28. 2.2 被测系统及相关信息
  29. 2.3 产品需求和设计文档
  30. 3. 测试安排
  31. 4. 测试结果汇总
  32. 4.1 测试用例执行情况
  33. 4.2 缺陷统计与分析
  34. 4.3 截图示例
  35. 5. 测试结论与建议
  36. 5.1 测试结论
  37. 5.2 后续建议
  38. 总结

更多推荐文章

查看全部
  • Spring Boot 远程调试配置方法
  • OpenClaw 本地部署指南:快速搭建自托管 AI 助手
  • Go2 机器人 ROS2 与 Gazebo 仿真环境搭建指南
  • ToDesk、顺网云与海马云部署 DeepSeek 对比评测
  • 本地部署 AI 绘画:Flux.1 模型快速实践指南
  • 3602. 十六进制和三十六进制转化——手写进制转换算法
  • C++ 类与对象全面剖析:构造函数深化与静态成员特性
  • C++ 单目操作符与标准输入输出详解
  • MySQL 索引原理:B+ 树演进与操作实战
  • 无人机视觉任务常用数据集汇总:检测与分割资源整理
  • GPT2-Chinese 中文文本生成模型实战指南
  • Erupt 低代码框架基于 Java 注解的企业级应用开发方案
  • 宇树机器人 G1 二次开发:基于 FAST-LIO 的建图与 RViz 配置
  • Flutter eip55 库鸿蒙适配指南:以太坊地址校验与验证
  • Obsidian Text Generator 插件使用指南
  • Python 数据结构详解:序列与集合
  • 使用 frontend-design Skill 提升大模型前端设计审美
  • C++ std::string 的弱点与潜在风险
  • 超越 Git:迈向数据驱动的机器学习模型版本管理
  • 算法基础:前缀和如何优化区间与子矩阵查询

相关免费在线工具

  • 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