“考测通”Web平台质量保障项目---测试报告

1. 项目背景

这个考试系统是一款基于 web 的在线测试平台。主要用于用户的知识水平考试和问卷调查,同时用户可在该平台进行知识题库的练习。为用户的知识学习和学习情况了解提供方便。该平台面向教育机构,企业培训部门和市场调研机构,提供从问卷和考试的创建、发布、答题到结果分析的全流程解决方案。

2. 项目简介

功能名称 功能描述 使用方法
用户管理 注册、登录、权限控制 打开网址,如果是新用户,可选择注册,如果已注册,可选择登录,可在"个人设置" 中修改密码等
问卷/考试管理 创建、编辑、发布、统计分析 在"我的项目"部分,可以选择考试/问卷的创建,编辑,发布和统计
练习系统 错题管理、练习记录 在"我的练习"部分,可以进行错题管理、练习记录
题库管理 试题创建、分类、导入导出 在"题库中心"部分,可以进行试题创建、分类、导入导出
模板管理 问卷/考试模板的创建添加共享 在"模版广场"部分,可以进行问卷/考试模板的创建添加共享
系统设置 界面风格、安全设置 在"系统管理"部分,可以进行界面风格、安全设置

3. 测试计划

3.1 测试范围

3.1.1 功能模块

  • 用户管理:注册、登录(用户/管理员)。
  • 问卷/考试管理:创建问卷,编辑问卷,创建考试,编辑考试,保存修改。
  • 项目管理:创建项目,搜索项目,回收站。
  • 练习系统:查询练习,练习状态的设置。
  • 题库中心:查询题库,新建题库,编辑题库,导出题库,问题管理
  • 模板管理:模板创建、保存、查找。
  • 系统设置:界面风格切换、密码的设置。

3.1.2 非功能特性

  • 兼容性:Chrome, Firefox, Edge;Windows 11。
  • 安全性:登录凭证安全、关键权限控制、删除操作 二次确认。
  • 性能:核心接口响应时间、并发支持(评估系统在100并发用户负载下的性能表现)。
  • 易用性:核心用户路径(注册->登录->考试->查看成绩)流畅性。

3.1.3 排除范围

  • IE11及以下浏览器。
  • MacOS 系统,(缺乏Apple设备,不在本次计划内)
  • 移动设备深度兼容性测试。(本次测试以桌面设备为主)
  • 渗透测试(不在本次计划内)。
  • 用户手册文档测试。

3.2 测试阶段

  1. 计划与设计
    编写计划、设计用例、准备数据、搭建环境、编写自动化框架/核心脚本。
  2. 功能测试执行
    执行手工测试、执行自动化测试,兼容性测试、记录缺陷。
  3. 性能测试
    设计场景、执行测试(并发登录/考试/提交)、监控分析、报告缺陷。
  4. 回归与验收
    缺陷验证、关键/受影响功能回归、最终冒烟、输出测试报告。

3.3 资源分配

3.3.1 人员:

  • 测试负责人: ethan (负责整体协调、计划、报告、风险管理)
  • 功能测试: ethan (手工测试、兼容性、基础安全)
  • 自动化/性能测试: ethan (自动化脚本、性能测试)

3.3.2 工具 :

  • 接口测试: Postman
  • 自动化测试: Selenium , Python
  • 性能测试: JMeter

3.3.3 环境 :

  • 测试服务器: http://8.155.1.153:8081/template
  • 测试PC: 1台 Windows 11
  • 浏览器: Chrome, Firefox, Edge

4. 测试工具

类别 工具名称 版本/备注
接口测试 Postman v11.57.1
自动化 Selenium 4.32.2
性能测试 JMeter 5.5

5. 测试动作/类型

本次针对考试系统的测试将涵盖以下主要测试类型,以确保系统功能完备、性能可靠、用户体验良好且具备一定的自动化回归能力:

5.1 功能测试

5.1.1 目标

验证考试系统所有核心功能模块(用户管理、问卷/考试管理、练习系统、题库管理、模板管理、系统设置)是否按照需求规格说明书和设计文档正确工作。

5.1.2 重点范围

5.1.2.1 正向流程

注册、登录、创建考试/问卷、添加试题、发布、考生答题、自动判分/阅卷、成绩查看统计、错题管理、模板应用、系统设置修改等核心流程是否顺畅。

5.1.2.2 反向/异常流程

输入验证(空值、非法格式、边界值、超长字符)、权限控制、操作容错(重复提交、网络中断恢复、并发操作冲突)、错误处理机制(友好提示)。

5.1.3 方法

主要采用手工测试,结合需求分析设计详细测试用例。


5.2 性能测试

5.2.1 目标

评估考试系统在高并发、大数据量下的稳定性、响应速度和资源消耗情况,识别性能瓶颈,确保满足预期负载要求。

5.2.2 重点场景

模拟了100个用户在5分钟内持续访问系统的核心业务流程。
业务流程: 脚本覆盖了用户从登录到访问"我的项目"、"我的练习"、"题库中心"等一系列关键操作。

5.2.3 关键指标

  • 接口响应时间。
  • 吞吐量。

5.2.4 方法

使用 JMeter 设计和执行性能测试脚本,模拟用户行为,监控服务器和数据库性能指标。


5.3 自动化测试

5.3.1 目标

提高回归测试效率,保证核心功能稳定性和快速反馈,支持持续集成。

5.3.2 范围

5.3.2.1 冒烟测试

部署后快速验证系统基本可用性(如:访问首页、用户登录、管理员登录)。

5.3.2.2 核心功能回归

覆盖高频、稳定、对业务影响大的核心正向流程:

  • 用户注册与登录(成功/失败)。
  • 创建基础考试/问卷并发布。
  • 模板广场创建模版。
  • 个人设置的修改

5.3.3 方法

使用 Selenium WebDriver + PyCharm 构建自动化测试脚本


5.4 兼容性测试

5.4.1 目标

确保考试系统在目标用户群体常用的浏览器和操作系统上功能正常、显示一致。

5.4.2 覆盖范围

5.4.2.1 浏览器

Chrome/Firefox/Edge

5.4.2.2 操作系统

Windows 11

5.4.3 方法

手工在不同浏览器-OS组合上执行核心功能测试用例(如登录、考试流程),观察UI布局和功能行为。使用浏览器开发者工具模拟不同分辨率。


5.6 易用性测试

5.6.1 目标

评估系统是否易于学习、使用和理解,用户体验是否流畅友好。

5.6.2 关键维度

  • 核心流程流畅性: 考生从登录到完成考试、查看成绩的步骤是否清晰、高效、无困惑?管理员创建考试/管理题库的操作是否直观?
  • 界面布局与导航: 功能入口是否明确?导航结构是否清晰?用户是否容易知道自己在哪里、能去哪里?
  • 信息提示: 操作反馈(成功/失败/等待)是否及时明确?错误提示是否清晰易懂,指导用户如何修正?表单校验提示是否友好?
  • 一致性: 界面风格、操作习惯、术语使用是否保持一致?

5.6.3 方法

测试人员模拟新手操作。

6. 具体测试实践

6.1 功能测试

功能测试-测试用例 脑图如下:


6.2 自动化测试

自动化测试脚本

  • 语言:Python 3.11
  • 框架:Pytest + Selenium WebDriver
  • 目录结构:

驱动类

python 复制代码
# 屏幕截图功能要用到的库  
import datetime  
import os  
import sys  
import traceback  
from calendar import month_name  
  
# selenium ,创建浏览器驱动要求用到的库  
from selenium import webdriver  
from selenium.webdriver import ActionChains, Keys  # 清空输入框要用到的库  
from selenium.webdriver.chrome.service import Service  
from webdriver_manager.chrome import ChromeDriverManager  
  
  
# 创建一个浏览器驱动类  
  
class Driver:  
    driver = ""  
    def __init__(self):  
        options = webdriver.ChromeOptions()  
        self.driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()), options = options)  
        # 隐式等待,作用域在这个类对象的整个生命周期  
        self.driver.implicitly_wait(3)  
  
# 屏幕截图  
    def getScreenshot(self,my_file_name=None):  
        # 后来修改点:屏幕截图老是保存不到,我现在需要构建一个绝对路径  
        # 1. 获取当前Util.py文件的绝对路径  
        current_file_path = os.path.abspath(__file__)  
        # 2. 获取其父目录  
        common_dir_path = os.path.dirname(current_file_path)  
        # 3. 找到项目根目录  
        project_dir = os.path.dirname(common_dir_path)  
        # 4. 用项目根目录和 'images' 文件夹名拼接出绝对路径  
        images_dir_path = os.path.join(project_dir, "images")  
  
        # 创建日期文件夹 日期文件夹为:x年-x月-x天  
        date_dir = os.path.join("images", datetime.datetime.now().strftime("%Y-%m-%d"))  
        if not os.path.exists(date_dir):  
            os.makedirs(date_dir, exist_ok=True)  
  
        if my_file_name:  
            method_name = my_file_name  
        else:  
            try:  
                # 正常情况下,自动获取方法名  
                method_name = sys._getframe().f_back.f_code.co_name  
            except Exception:  
                # 如果获取失败就给一个我自己定义的名字  
                method_name = "不知道哪里出错嘞呀~"  
  
        # 生成截图的文件名,格式为:调用函数名-时分秒.png  
        file_name = method_name + "-" + datetime.datetime.now().strftime("%H%M%S") + ".png"  
  
        # 拼接完整路径并保存截图  
        file_path = os.path.join(date_dir, file_name)  
        self.driver.save_screenshot(file_path)  
        print(f"截图成功,文件名: '{file_name}' 中保存截图至: {file_path}")  
  
# 清空输入框  
    def clear(self, element):  
        """模拟真人操作清空输入框。"""  
        actions = ActionChains(self.driver)  
        actions.click(element)  
        actions.key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL)  # 按下 ctrl + a 全选  
        actions.send_keys(Keys.DELETE)  # 然后在删除  
        actions.perform()  # 执行所有动作  
# 退出浏览器  
    def quit(self):  
        """提供一个关闭浏览器的方法。"""  
        print("---  正在关闭全局浏览器... ---")  
        self.driver.quit()  
  
ethan_driver = Driver()

注册模块测试类

测试用例
代码如下:
python 复制代码
import time  
  
from selenium.webdriver.common.by import By  
  
# 导入浏览器对象  
from common.Utils import ethan_driver  
  
# 创建专门用于测试 "注册" 页面的类  
class RegisterTest:  
    # 初始化  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        self.register_url = "http://8.155.1.153:8081/user/register"  
        self.driver.get(self.register_url)  
        print("正在打开网址~~~~~~~~~~~~~~~")  
        time.sleep(1)  
  
  
    # 正向流程 : 清空输入框,输入账号和密码是否能注册成功并跳转到登录界面  
    def RegisterSuccessTest(self):  
        print("正向流程 : 清空输入框,输入账号和密码是否能注册成功并跳转到登录界面")  
        # 先清空输入框  
        ethan_driver.clear(self.driver.find_element(By.CSS_SELECTOR, "#name"))  
        ethan_driver.clear(self.driver.find_element(By.CSS_SELECTOR, "#username"))  
        ethan_driver.clear(self.driver.find_element(By.CSS_SELECTOR, "#password"))  
        ethan_driver.clear(self.driver.find_element(By.CSS_SELECTOR, "#rePassword"))  
  
        # 输入信息  
        self.driver.find_element(By.CSS_SELECTOR, "#name").send_keys("ethan888")  
        self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("ethan888")  
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123456")  
        self.driver.find_element(By.CSS_SELECTOR, "#rePassword").send_keys("123456")  
        # 点击注册  
        self.driver.find_element(By.CSS_SELECTOR, "#root > div > div.content___1k5Ro > div.main___19HXK > div > form > button").click()  
        time.sleep(1)  
  
        # 验证是否能跳转到登录界面  
        login_url = "http://8.155.1.153:8081/user/login"  
        assert self.driver.current_url == login_url  
        # 截图以记录成功状态  
        ethan_driver.getScreenshot()  
  
    # 反向/异常流程  
    ## 输入一个已经存在的账号(如admin),期望注册失败  
    def register_fail_count(self):  
        print("反向/异常流程 : 输入一个已经存在的账号(如admin),期望注册失败")  
        # 先重新进入一下url,保证环境干净  
        self.driver.get(self.register_url)  
        time.sleep(1)  
  
        # 输入信息  
        self.driver.find_element(By.CSS_SELECTOR, "#name").send_keys("admin")  
        self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("admin")  
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123456")  
        self.driver.find_element(By.CSS_SELECTOR, "#rePassword").send_keys("123456")  
        self.driver.find_element(By.CSS_SELECTOR,"#root > div > div.content___1k5Ro > div.main___19HXK > div > form > button").click()  
        time.sleep(1)  
  
        # 截图记录失败场景  
        ethan_driver.getScreenshot()  
  
        # 验证  
        abc = self.driver.find_element(By.CSS_SELECTOR, "#root > div > div.content___1k5Ro > div.main___19HXK > div > form > div.ant-alert.ant-alert-error > div > div").text  
        assert abc == "账号已存在"  
  
    ## 两次输入的密码一致,希望注册失败  
    def register_fail_passwd(self):  
        print("反向/异常流程 : 输入不一致的密码,期望注册失败")  
        # 重进网址,保持环境干净  
        self.driver.get(self.register_url)  
        time.sleep(1)  
  
        # 输入信息  
        self.driver.find_element(By.CSS_SELECTOR, "#name").send_keys("ethan666")  
        self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("ethan666")  
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123789")  
        self.driver.find_element(By.CSS_SELECTOR, "#rePassword").send_keys("123456")  
        self.driver.find_element(By.CSS_SELECTOR, "#root > div > div.content___1k5Ro > div.main___19HXK > div > form > button").click()  
        time.sleep(1)  
  
        # 验证  
        massge = self.driver.find_element(By.CSS_SELECTOR, "#rePassword_help > div").text  
        # 截图记录  
        ethan_driver.getScreenshot()  
        # 断言  
        assert massge == "两次密码不一致!"  
  
    ## 不输入用户名、但输入登陆账号、密码、确认密码后进行注册  
    def register_fail_no_name(self):  
        print("不输入用户名、但输入登陆账号、密码、确认密码后进行注册")  
        # 重进网址,保持环境干净  
        self.driver.get(self.register_url)  
        time.sleep(1)  
  
        # 输入信息  
        self.driver.find_element(By.CSS_SELECTOR, "#name").send_keys("")  
        self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("ethan666")  
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123789")  
        self.driver.find_element(By.CSS_SELECTOR, "#rePassword").send_keys("123456")  
        self.driver.find_element(By.CSS_SELECTOR, "#root > div > div.content___1k5Ro > div.main___19HXK > div > form > button").click()  
        time.sleep(1)  
        # 检查是否注册失败  
        massge = self.driver.find_element(By.CSS_SELECTOR, "#name_help > div").text  
        # 添加屏幕截图  
        ethan_driver.getScreenshot()  
        # 断言检测一下是否符合预期  
        assert massge == "用户名是必填项!"

登录模块测试类

自动化测试用例:
代码如下:
python 复制代码
import time  
  
from selenium.webdriver.common.by import By  
from common.Utils import ethan_driver  
  
# 脚本执行太快了,网页还没反应过来,我们这里要加入显示等待了  
from selenium.webdriver.support.ui import WebDriverWait  
from selenium.webdriver.support import expected_conditions as EC  
  
  
  
  
class LoginTest:  
    def __init__(self):  
  
        #############################   元素定位  ############################        # 将登录页面的元素位置都放在专门的元组里,如果以后变了的话,方便我们修改  
        self.driver = ethan_driver.driver  
        self.login_url = "http://8.155.1.153:8081/user/login"  
  
        # 用户名输入框定位  
        self.username_input = (By.CSS_SELECTOR, "#username")  
        # 密码输入框定位  
        self.password_input = (By.CSS_SELECTOR, "#password")  
        # 登录按钮定位  
        self.login_button = (By.CSS_SELECTOR, "#root > div > div.content___2zk1- > div.main___x4OjT > div > form > button")  
  
        # 登录成功之后,页面右上角的用户昵称定位  
        self.nickname = (By.XPATH, "//span[text()='Admin']")  
  
        # 登录失败之后,错误提示框的定位  
        self.error_kuang = (By.CSS_SELECTOR, "#root > div > div.content___2zk1- > div.main___x4OjT > div > form > div.ant-alert.ant-alert-error > div > div")  
  
        # 用户名必填 错误提示框定位  
        self.username_must_write_error = (By.CSS_SELECTOR,"#username_help > div")  
  
        # 密码必填 错误提示框  
        self.passwd_must_write_error = (By.CSS_SELECTOR,"#password_help > div")  
  
    #############################   页面操作  ############################    # 封装一些通用的、重复性的操作,让测试用例代码更简洁  
    def login_work(self,username,password):  
        print(f"----------- 正在使用账号: {username},密码:: {password},进行登录......")  
        self.driver.get(self.login_url)  
        time.sleep(1)  
  
        # 清空输入框  
        ethan_driver.clear(self.driver.find_element(*self.username_input))  
        ethan_driver.clear(self.driver.find_element(*self.password_input))  
  
        # 输入用户名和密码  
        if username:  
            self.driver.find_element(*self.username_input).send_keys(username)  
        if password:  
            self.driver.find_element(*self.password_input).send_keys(password)  
        time.sleep(1)  
  
        # 点击登录按钮  
        self.driver.find_element(*self.login_button).click()  
  
    ######################     测试用例        #############################  
    # 正向流程 : 使用正确的管理员账号和密码登录  
    def login_scuess(self):  
        print("正向流程 : 使用正确的管理员账号和密码登录")  
        self.login_work("admin","123456")  
  
        # 验证,检查昵称是否是 admin        try:  
            checkadmin = self.driver.find_element(*self.nickname).text  
            assert checkadmin == "Admin"  
            print("---  断言成功!在页面上找到了欢迎语 'Admin'。")  
            ethan_driver.getScreenshot()  
  
        except Exception as e:  
            print(f"---断言失败,登录后没有找到预期的话。错误{e}")  
            ethan_driver.getScreenshot()  
            raise  
        print("---测试通过-----")  
  
    # 反向/异常流程  
    ## 1. 输入错误的用户名和错误的密码  
    def login_error_name_or_passwd(self):  
        print("\n---【开始测试】: 错误用户名和密码场景 ---")  
        self.login_work("我最帅","红豆泥最帅")  
        # 验证是否出现了错误提示框  
        kuang = self.driver.find_element(*self.error_kuang).text  
  
        # 新增调试代码 : 在断言前,打印出我们实际从页面上抓取到的内容,老是报错,人都疯了,我写的是"或",原来是"和",  
       # print(f"--- [调试信息] 页面上实际的错误提示是: '{kuang}'")  
        assert kuang == "错误的用户名和密码"  
        print(f"---  断言成功!页面提示: '{kuang}'")  
        ethan_driver.getScreenshot()  
        print("---【测试通过】---")  
    ## 2. 输入正确的用户名和错误的密码  
    def login_wrong_password(self):  
        print("\n---【开始测试】: 正确用户名和错误密码场景 ---")  
        self.login_work("admin","111111")  
  
        # 验证是否出现了错误提示框  
        kuang = self.driver.find_element(*self.error_kuang).text  
        #print(f"--- [调试信息] 页面上实际的错误提示是: '{kuang}'")  
        assert kuang == "错误的用户名和密码"  
        print(f"---  断言成功!页面提示: '{kuang}'")  
        ethan_driver.getScreenshot()  
        print("---测试通过---")  
    ## 3. 不输入用户名,只输入密码  
    def login_no_username(self):  
        print("\n不输入用户名,只输入密码")  
        self.login_work("","123456")  
  
        try:  
            # 这里脚本执行太快了,可网页又不能反应过来,所以我加入了显示等待  
            wait = WebDriverWait(self.driver, 5) # 创建一个等待器,最多等5秒  
            # 调用 until 方法,等待我们期望的条件发生  
            #    EC.text_to_be_present_in_element 这个条件的意思是:  
            #    在 self.username_must_write_error 这个元素里,出现了"用户名是必填项!"这段文字  
            wait.until(EC.text_to_be_present_in_element(  
                self.username_must_write_error,  
                "用户名是必填项!"  
            ))  
            # 如果上面这行代码能成功执行没有报错,就说明断言已经成功了!  
            print("---- 断言成功,页面按预期提示:'用户名是必填项!'")  
            ethan_driver.getScreenshot(self.login_no_username.__name__)  # 截图时传入方法名  
        except Exception as e:  
            print(f"!!! 测试失败,在规定时间内未找到预期的错误提示。错误: {e}")  
            ethan_driver.getScreenshot(f"{self.login_no_username.__name__}_FAILED")  
            raise  # 仍然抛出异常,让测试最终失败  
        print("------------ 测试3. 不输入用户名,只输入密码通过 ------------")  
  
  
    ## 4. 输入用户名,不输入密码  
    def login_no_password(self):  
        print("\n不输入密码,只输入用户名")  
        self.login_work("admin","")  
  
        # 这里同上,也要加入显示等待  
        try:  
            wait = WebDriverWait(self.driver, 5)  
            wait.until(EC.text_to_be_present_in_element(self.passwd_must_write_error,"密码是必填项!"))  
            print("---- 断言成功,页面按预期提示:'密码是必填项!'")  
            ethan_driver.getScreenshot(self.login_no_password.__name__)  
        except Exception as e:  
            print(f"测试失败,页面没有按预期显示,错误:{e}")  
            ethan_driver.getScreenshot(f"{self.login_no_password.__name__}_FAILED")  
            raise  
        print("------------- 测试4. 输入用户名,不输入密码 通过----------------")

首页功能测试类

自动化测试用例:
自动化测试脚本
python 复制代码
from selenium.webdriver.support import expected_conditions as EC  
from selenium.common import TimeoutException  
from selenium.webdriver.common.by import By  
from selenium.webdriver.support.wait import WebDriverWait  
  
from common.Utils import ethan_driver # 导入浏览器驱动对象  
from tests.login_func import auto_login # 导入自动登录函数  
  
  
class homeTest:  
  
    # 构造函数  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        # 先登录,先登录才能定位元素  
        auto_login(self.driver,"admin","123456")  
  
  
        # --------------   先找到我们要测试的元素  ------------------  
        # 1. "首页" 定位  
        self.title_text = (By.XPATH,"//div[contains(@class,'heading')]//span[text()='首页']")  
        # 2. "问卷数" 和 "考试数" 这两个统计项的标题 定位  
        self.wen_juan_shu = (By.XPATH,"//div[text()='问卷数']")  
        self.kao_shi_shu = (By.XPATH,"//div[text()='问卷数']")  
        # 3. "我的考试","我的问卷","问卷记录","考试记录"  
        self.my_exam = (By.ID,"rc-tabs-0-tab-exam")  
        self.my_wen_juan = (By.ID,"rc-tabs-0-tab-survey")  
        self.wen_juan_jilu = (By.ID,"rc-tabs-0-tab-surveyHistory")  
        self.kao_shi_jilu = (By.ID,"rc-tabs-0-tab-examHistory")  
        # 4. 两创建按钮:"创建问卷","创建考试"  
        self.create_wenjuan = (By.XPATH,"//a[contains(.,'创建问卷')]")  
        self.create_exam = (By.XPATH,"//a[contains(.,'创建考试')]")  
  
  
    # 测试用例  
    def test_home_element(self):  
        print("--------- 开始进行【首页】的测试---------------")  
        try:  
            # 创建一个显示等待,因为页面需要加载  
            wait = WebDriverWait(self.driver, 10) # 最多等待十秒  
            # 验证页面标题  
            print("---  正在验证: 页面标题...")  
            wait.until(EC.visibility_of_element_located(self.title_text))  
  
            # 验证问卷数  
            print("---  正在验证: 问卷数...")  
            wait.until(EC.visibility_of_element_located(self.wen_juan_shu))  
  
            # 验证 考试数  
            print("---  正在验证: 考试数...")  
            wait.until(EC.visibility_of_element_located(self.kao_shi_shu))  
  
  
            # 验证 我的考试  
            print("---  正在验证: 我的考试Tab...")  
            wait.until(EC.visibility_of_element_located(self.my_exam))  
  
            # 验证 我的问卷  
            print("---  正在验证: 我的问卷Tab...")  
            wait.until(EC.visibility_of_element_located(self.my_wen_juan))  
  
  
            # 验证 问卷记录  
            print("---  正在验证: 问卷记录...")  
            wait.until(EC.visibility_of_element_located(self.wen_juan_jilu))  
  
            # 验证 考试记录  
            print("---  正在验证: 考试记录T...")  
            wait.until(EC.visibility_of_element_located(self.kao_shi_jilu))  
  
            # 验证 创建问卷  
            print("---  正在验证: 创建问卷按钮...")  
            wait.until(EC.visibility_of_element_located(self.create_wenjuan))  
  
            # 验证 创建考试  
            print("---  正在验证: 创建考试按钮...")  
            wait.until(EC.visibility_of_element_located(self.create_exam))  
  
            # 如果所有的都成功了,没有抛异常,说明元素都找到了  
            print("---  断言成功!所有首页核心元素均已成功显示。")  
            ethan_driver.getScreenshot(self.test_home_element.__name__)  
  
  
  
        except Exception as e:  
            print(f"-------  测试失败,首页上面有元素不在 ------- 报错{e}")  
            ethan_driver.getScreenshot(self.test_home_element.__name__)  
            raise  
  
  
        print("----- 【首页测试通过】-------")

页面:"我的项目"测试类

自动化测试用例
自动化测试脚本
python 复制代码
import time # 添加强制等待,在验证页面元素是否存在的时候,总是出错,用强制等待来帮助解决问题  
  
  
  
from selenium.webdriver.common.by import By  
from selenium.webdriver.support.wait import WebDriverWait # 等待  
  
from common.Utils import ethan_driver # 导如何浏览器驱动对象  
from tests.login_func import auto_login  
  
from selenium.webdriver.support import expected_conditions as EC  
  
class ethan_projectTest:  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        self.wait = WebDriverWait(self.driver, 10)  
  
        # 登录并跳转界面  
        try:  
            print("------------- 准备登录,并跳转到"我的项目"页面----------------")  
            auto_login(self.driver,"admin","123456")  
            target_url = "http://8.155.1.153:8081/project"  
            self.driver.get(target_url)  
            # 验证跳转  
            check_get = (By.XPATH,"//div[contains(@class,'ant-page-header-heading')]//span[text()='我的项目']")  
            self.wait.until(EC.visibility_of_element_located(check_get))  
            print("----------- 登录并跳转成功 ----------------")  
        except Exception as e:  
            print("登录跳转失败,错误:{e}")  
            ethan_driver.getScreenshot("ethan_projectTest_登录跳转页面失败")  
            raise  
  
        # ------------------------- 页面元素定位 -----------------------  
        # 1. 页面标题元素  
        self.title = (By.XPATH,"//div[contains(@class,'ant-page-header-heading')]//span[text()='我的项目']")  
  
  
        # 2.新建按钮  
        self.new_create_button = (By.XPATH,"//button[contains(.,'新建')]")  
  
  
        # 3.搜索框  
        self.find_kuang = (By.CSS_SELECTOR,"input[placeholder='搜索项目名称']")  
  
        # 4.搜索按钮  
        self.find_button = (By.CSS_SELECTOR, ".ant-input-search-button")  
  
        # 5. 新建后弹出的"问卷调查"选项  
        self.new_wenjuan_option = (By.XPATH,"//li[contains(@class, 'ant-dropdown-menu-item')]//span[text()='问卷调查']")  
  
        # 6. 新建在线考试  
        self.new_exam_option = (By.XPATH,"//li[contains(@class,'ant-dropdown-menu-item')]//span[text()='在线考试']")  
  
        # 新增一个内部方法,用于返回"我的项目"页面并验证  
    def _return_to_project_page(self):  
        # 由于脚本执行的太快,ethan_driver.back 根本不行,所以手写一个返回功能  
        print("--- 正在导航回'我的项目'页面 ---")  
        target_url = "http://8.155.1.153:8081/project"  
        self.driver.get(target_url)  
        # 等待并验证页面标题,确保我们真的回来了  
        self.wait.until(EC.visibility_of_element_located(self.title))  
        print("--- 已成功返回'我的项目'页面 ---")  
  
  
    # ----------------------  执行测试用例 -------------------------------------------    # 测试用例 1 : 检查页面相关元素是否存在  
    def element_is_there(self):  
        print("--------- 开始测试用例 1 : 检查页面相关元素是否存在---------------")  
        try:  
            # 验证页面标题  
            print("---  正在验证: 页面标题...")  
            self.wait.until(EC.visibility_of_element_located(self.title))  
            print("---  页面标题有...")  
            # 验证新建按钮  
            print("---  正在验证: 新建按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.new_create_button))  
            print("--- 新建按钮有...")  
            # 验证新建按钮  
            print("---  正在验证: 搜索框...")  
            self.wait.until(EC.visibility_of_element_located(self.find_kuang))  
            print("---  搜索框有...")  
            # 验证新建按钮  
            print("---  正在验证:搜索按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.find_button))  
            print("---  搜索按钮有...")  
  
        except Exception as e:  
            print(f"这个页面缺少关键元素,错误:{e}")  
            ethan_driver.getScreenshot("页面缺少关键元素")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("----------- 测试用例 1 通过 页面关键元素都存在 ----------------")  
    # 测试用例 2 : 新建一个调查问卷  
    def create_wen_juan_test(self):  
        print("-------------- 进行测试用例 2 : 新建一个调查问卷-----------------")  
        # 测试流程:  
        try:  
            # 先确保我们在 我们的项目界面  
            self._return_to_project_page()  
            # 1. 点击新建按钮  
            self.driver.find_element(*self.new_create_button).click()  
            ethan_driver.getScreenshot("点击新建按钮")  
            # 2. 选择调查问卷  
            wen_juan_button_option = self.wait.until(EC.element_to_be_clickable(self.new_wenjuan_option))  
            wen_juan_button_option.click()  
            ethan_driver.getScreenshot("选择调查问卷")  
            # 3. 点击保存  
            save_button = (By.XPATH,"//button/span[text()='保 存']")  
            save_button_option = self.wait.until(EC.element_to_be_clickable(save_button))  
            save_button_option.click()  
            ethan_driver.getScreenshot("点击保存")  
  
            # 4. 返回项目页面  
            self.driver.back()  
            ethan_driver.getScreenshot("返回项目页面")  
        except Exception as e:  
            print("----- 新建调查问卷失败-----------")  
            ethan_driver.getScreenshot("新建调查问卷失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
  
    # 测试用例 3 : 新建一个在线考试  
    def create_exam_test(self):  
        print("--------------- 进行测试用例 3 : 新建一个在线考试-----------")  
        try:  
            # 先确保我们在 我们的项目界面  
            self._return_to_project_page()  
            # 点击新建按钮  
            self.driver.find_element(*self.new_create_button).click()  
            ethan_driver.getScreenshot("点击新建按钮")  
            # 选择 在线考试  
            new_exam_button_option = self.wait.until(EC.element_to_be_clickable(self.new_exam_option))  
            new_exam_button_option.click()  
            ethan_driver.getScreenshot("点击新建在线考试")  
            # 点击保存  
            save_button = (By.XPATH,"//button/span[text()='保 存']")  
            save_button_option = self.wait.until(EC.element_to_be_clickable(save_button))  
            save_button_option.click()  
            ethan_driver.getScreenshot("保存在线考试")  
  
        except Exception as e:  
            print("------------ 新建在线考试失败 ----------------")  
            ethan_driver.getScreenshot("新建在线考试失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise

页面:"我的题库"测试类

自动化测试用例
自动化测试脚本
python 复制代码
import time  
  
from selenium.webdriver.support.wait import WebDriverWait  
  
from common.Utils import ethan_driver # 导入浏览器驱动对象  
from tests.login_func import auto_login  
from selenium.webdriver.common.by import By  
from selenium.webdriver.support import expected_conditions as EC  
import datetime  # 需要先导入datetime库  
  
class ethan_question_Test:  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        self.wait = WebDriverWait(self.driver, 10)  
  
        # 登录并跳转界面  
        try:  
            print("------------开始登录跳转到"我的题库"----------")  
            # 登录  
            auto_login(self.driver,"admin","123456")  
            # 跳转  
            target_url = "http://8.155.1.153:8081/repo/index"  
            self.driver.get(target_url)  
            # 验证跳转  
            check_get = (By.XPATH,"//span[contains(@class,'ant-breadcrumb-link')]//span[text()='我的题库']")  
            self.wait.until(EC.visibility_of_element_located(check_get))  
            print("------------登录跳转到"我的题库"成功----------")  
  
        except Exception as e:  
            print(f"------------登录跳转到"我的题库"失败"----------,错误:{e}")  
            ethan_driver.getScreenshot("登录跳转到"我的题库"失败")  
            raise  
  
        # -------------------- 定位页面相关元素 -------------------------------------  
        # 页面标题  
        self.title = (By.XPATH,"//span[contains(@class,'ant-breadcrumb-link')]//span[text()='我的题库']")  
  
        # 名称输入框  
        self.name_input_kuang = (By.CSS_SELECTOR,"#name")  
  
        # 类型选择框标  
        self.lei_xing_choice_kuang = (By.CSS_SELECTOR,"#mode")  
        # 重置按钮  
        self.reback_button = (By.XPATH,"(//button[contains(@class, 'ant-btn-default')])[1]")  
  
        # 查询按钮  
        self.select_button = (By.XPATH,"(//button[contains(@class, 'ant-btn ant-btn-primary')])[1]")  
  
  
        # 新建按钮  
        self.new_create = (By.XPATH,'//*[@id="sk-layout"]/div/div/section/div[2]/main/div/div[2]/div/div/div/div[2]/div/div[1]/div/div[2]/div[1]/div/div[1]/button')  
  
        # 导出按钮  
        self.out_button = (By.XPATH,'//*[@id="sk-layout"]/div/div/section/div[2]/main/div/div[2]/div/div/div/div[2]/div/div[1]/div/div[2]/div[1]/div/div[2]/button/span[2]')  
  
        # 题库列表  
        self.list_ti_ku = (By.XPATH,'//*[@id="sk-layout"]/div/div/section/div[2]/main/div/div[2]/div/div/div/div[2]/div/div[1]/div/div[1]/div')  
  
        # 文本导入按钮  
        self.text_daoru_button = (By.XPATH,'//*[@id="sk-layout"]/div/div/section/div[2]/main/div/div[2]/div/div/div/div[2]/div/div[2]/div/div/div/div/div/table/tbody/tr[2]/td[9]/div/div[1]/a')  
  
        # 试题管理按钮  
        self.manager_exam_button = (By.CSS_SELECTOR,'//*[@id="sk-layout"]/div/div/section/div[2]/main/div/div[2]/div/div/div/div[2]/div/div[2]/div/div/div/div/div/table/tbody/tr[2]/td[9]/div/div[3]/a')  
  
    # 新增一个内部方法,用于返回"我的题库"页面并验证  
    def _return_target_page(self):  
        print("--- 正在导航回'我的题库'页面 ---")  
        target_url = "http://8.155.1.153:8081/repo/index"  
        self.driver.get(target_url)  
        # 等待并验证页面标题,确保我们真的回来了  
        self.wait.until(EC.visibility_of_element_located(self.title))  
        print("--- 已成功返回'我的项目'页面 ---")  
  
    # ----------------------  执行测试用例 -------------------------------------------    # 测试用例 1 : 检查页面相关元素是否存在  
    def element_is_there(self):  
        print("--------- 开始测试用例 1 : 检查页面相关元素是否存在---------------")  
        try:  
            # 验证页面标题  
            print("---  正在验证: 页面标题...")  
            self.wait.until(EC.visibility_of_element_located(self.title))  
  
            # 名称输入框  
            print("---  正在验证: # 名称输入框...")  
            self.wait.until(EC.visibility_of_element_located(self.name_input_kuang))  
  
            # 重置按钮  
            print("---  正在验证: # 重置按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.reback_button))  
  
            # 查询按钮  
            print("---  正在验证: # 查询按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.select_button))  
  
            # 新建按钮  
            print("---  正在验证: # 新建按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.new_create))  
  
            # 导出按钮  
            print("---  正在验证: #导出按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.out_button))  
  
            # 题库列表  
            print("---  正在验证: #题库列表...")  
            self.wait.until(EC.visibility_of_element_located(self.list_ti_ku))  
  
            # 文本导入按钮  
            print("---  正在验证: #文本导入按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.text_daoru_button))  
  
  
        except Exception as e:  
            print(f"这个页面缺少关键元素,错误:{e}")  
            ethan_driver.getScreenshot("页面缺少关键元素")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
  
    # ------------------- 测试用例 2 ,新建一个题库  
    def create_ti_ku(self):  
        try:  
            print("--------- 开始测试用例 2 : 新增并查询一个题库---------------")  
            # 先确保我们在初始页面---我的题库  
            self._return_target_page()  
  
            # 点击新建  
            click_new_create = self.wait.until(EC.element_to_be_clickable(self.new_create))  
            click_new_create.click()  
            ethan_driver.getScreenshot("点击新建")  
  
            # 输入题库名  
                # 为了每次输入不一样的名字,我们这里要引入时间来命名  
            now = datetime.datetime.now()  
                # 把它格式化成我们想要的字符串格式,比如 "年-月-日_时分秒"  
            timestamp = now.strftime("%Y-%m-%d_%H%M%S")  
                # 创建一个独一无二的题库名称  
            unique_name = f"自动化测试题库_{timestamp}"  
                # 找到输入框,输入题库名  
            xiao_ti_ku_input_kuang = (By.CSS_SELECTOR,"//div[contains(@class, 'ant-drawer-open')]//label[@title='题库类型']/../following-sibling::div//div[contains(@class, 'ant-select-selector')]")  
            shuru_ti_ku_name = self.wait.until(EC.visibility_of_element_located(xiao_ti_ku_input_kuang))  
            shuru_ti_ku_name.send_keys(unique_name)  
  
            # 选择题库类型  
            xiao_lei_xing_choice_kuang = (By.CSS_SELECTOR,'.ant-drawer-body #mode')  
            choice_lei_xing = self.wait.until(EC.element_to_be_clickable(xiao_lei_xing_choice_kuang))  
            choice_lei_xing.click()  
  
  
            # print("--- 脚本已暂停20秒,去浏览器检查下拉选项的HTML! ---")            # time.sleep(120)  
  
            choice_lei_xing_wenjuan = (By.XPATH, "//div[contains(@class, 'ant-select-dropdown')]//div[@class='ant-select-item-option-content' and text()='问卷']")  
            wait_choice_leix_xing_wenjuan = self.wait.until(EC.element_to_be_clickable(choice_lei_xing_wenjuan))  
            wait_choice_leix_xing_wenjuan.click()  
  
            # 点击确定  
            self.driver.find_element(By.XPATH,"//div[@class='ant-drawer-footer']//button[contains(., '确定')]").click()  
  
            # 注释:此代码存在一个问题,自动化运行脚本无法点击下拉框中的问卷来新建问卷
  
            # 查询验证  
            # 1.点击名称搜索框,输入刚才创建的题库名  
  
            # 2.点击类型框,选择题库类型  
  
            # 3.点击查询按钮进行查询  
  
  
        except Exception as e:  
            print(f"--新增题库失败,错误:{e}")  
            ethan_driver.getScreenshot("新增题库失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise

页面:"模板广场"测试类

自动化测试用例
自动化测试脚本
python 复制代码
import time  
from selenium.webdriver.common.by import By  
from selenium.webdriver.support.wait import WebDriverWait  
from selenium.webdriver.support import expected_conditions as EC  
  
# 导入您的浏览器驱动对象和登录函数  
from common.Utils import ethan_driver  
from tests.login_func import auto_login  
  
  
class ethan_templateTest:  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        self.wait = WebDriverWait(self.driver, 10)  
        self.template_name = f"自动化测试模板_{int(time.time())}"  
  
        # 登录并跳转界面  
        try:  
            print("------------- 准备登录,并跳转到"模板广场"页面----------------")  
            auto_login(self.driver, "admin", "123456")  
            target_url = "http://8.155.1.153:8081/template"  
            self.driver.get(target_url)  
            # 验证跳转  
            check_get = (By.XPATH, "//span[contains(@class, 'ant-page-header-heading-title') and text()='模板广场']")  
            self.wait.until(EC.visibility_of_element_located(check_get))  
            print("----------- 登录并跳转成功 ----------------")  
        except Exception as e:  
            print(f"登录跳转失败,错误:{e}")  
            ethan_driver.getScreenshot("ethan_templateTest_登录跳转页面失败")  
            raise  
  
        # ------------------------- 页面元素定位 -----------------------        self.page_title = (By.XPATH, "//span[contains(@class, 'ant-page-header-heading-title') and text()='模板广场']")  
        self.search_input = (By.XPATH, "//input[@placeholder='输入模板名字检索']")  
        self.search_button = (By.XPATH, "//button[.//span[text()='搜模板']]")  
        self.add_template_button = (By.XPATH, "//button[.//span[text()='添加模板']]")  
        self.public_repo_tab = (By.XPATH, "//div[@class='ant-tabs-tab-btn' and text()='公共库']")  
        self.private_repo_tab = (By.XPATH, "//div[@class='ant-tabs-tab-btn' and text()='私有库']")  
        self.category_filter = (By.XPATH, "//form//span[text()='分类']")  
        self.tags_filter = (By.XPATH, "//form//span[text()='标签']")  
  
        # --- 新建模板编辑器内元素 ---        self.editor_save_button = (By.XPATH,  
                                   "//div[@class='survey-main-panel-toolbar']//button[.//span[normalize-space()='保 存']]")  
        self.editor_close_button = (By.XPATH,  
                                    "//div[@class='survey-main-panel-toolbar']//button[.//span[normalize-space()='关 闭']]")  
  
        # --- 保存模板弹窗内元素 ---        self.modal_template_name_input = (By.CSS_SELECTOR, "form #name")  
        self.modal_category_input = (By.CSS_SELECTOR, "form #category")  
        self.modal_add_to_public_checkbox = (By.XPATH,  
                                             "//label[.//span[text()='添加到公共库']]//input[@type='checkbox']")  
        self.modal_confirm_save_button = (By.XPATH,  
                                          "//div[@class='ant-modal-footer']//button[.//span[normalize-space()='保 存']]")  
  
    def _return_to_template_page(self):  
        """内部方法,用于返回"模板广场"页面并验证"""  
        print("--- 正在导航回'模板广场'页面 ---")  
        target_url = "http://8.155.1.153:8081/template"  
        self.driver.get(target_url)  
        self.wait.until(EC.visibility_of_element_located(self.page_title))  
        print("--- 已成功返回'模板广场'页面 ---")  
  
    # ----------------------  执行测试用例 -------------------------------------------    def element_is_there(self):  
        """测试用例 1 : 检查页面相关元素是否存在"""  
        print("--------- 开始测试用例 1 : 检查页面相关元素是否存在---------------")  
        try:  
            print("---  正在验证: 页面标题...")  
            self.wait.until(EC.visibility_of_element_located(self.page_title))  
            print("---  正在验证: 模板名称输入框...")  
            self.wait.until(EC.visibility_of_element_located(self.search_input))  
            print("---  正在验证: 搜模板按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.search_button))  
            print("---  正在验证: 添加模板按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.add_template_button))  
            print("---  正在验证: 公共库Tab...")  
            self.wait.until(EC.visibility_of_element_located(self.public_repo_tab))  
            print("---  正在验证: 私有库Tab...")  
            self.wait.until(EC.visibility_of_element_located(self.private_repo_tab))  
  
        except Exception as e:  
            print(f"这个页面缺少关键元素,错误:{e}")  
            ethan_driver.getScreenshot("模板广场页面缺少关键元素")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("----------- 测试用例 1 通过:页面关键元素都存在 ----------------")  
  
    def create_template_test(self):  
        """测试用例 2: 新增一个模板"""  
        print("\n--------- 开始测试用例 2 : 新增一个模板 ---------------")  
        try:  
            self._return_to_template_page()  
  
            print("--- 正在点击'添加模板'按钮...")  
            self.wait.until(EC.element_to_be_clickable(self.add_template_button)).click()  
            ethan_driver.getScreenshot("点击添加模板")  
  
            print("--- 正在点击编辑器中的'保存'按钮...")  
            self.wait.until(EC.element_to_be_clickable(self.editor_save_button)).click()  
            ethan_driver.getScreenshot("进入模板编辑器后点击保存")  
  
            print(f"--- 正在输入模板名称: {self.template_name}")  
            self.wait.until(EC.visibility_of_element_located(self.modal_template_name_input)).send_keys(  
                self.template_name)  
  
            print("--- 正在输入分类: '自动化分类'")  
            self.driver.find_element(*self.modal_category_input).send_keys("自动化分类")  
  
            print("--- 正在勾选'添加到公共库'")  
            self.driver.find_element(*self.modal_add_to_public_checkbox).click()  
            ethan_driver.getScreenshot("填写模板保存信息")  
  
            print("--- 正在点击弹窗中的'保存'按钮...")  
            self.driver.find_element(*self.modal_confirm_save_button).click()  
  
            print("--- 等待保存弹窗消失...")  
            self.wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "save-to-template")))  
            print("--- 模板保存成功!")  
            ethan_driver.getScreenshot("模板保存成功")  
  
            print("--- 正在关闭模板编辑器...")  
            self.wait.until(EC.element_to_be_clickable(self.editor_close_button)).click()  
  
            print("--- 正在验证列表中是否存在新模板...")  
            self.wait.until(EC.visibility_of_element_located(self.page_title))  
            new_template_locator = (By.XPATH, f"//div[@class='ant-card-meta-title']/a[text()='{self.template_name}']")  
            self.wait.until(EC.presence_of_element_located(new_template_locator))  
            assert self.driver.find_element(*new_template_locator).is_displayed()  
            print(f"--- 验证成功,在列表中找到模板: {self.template_name}")  
  
        except Exception as e:  
            print(f"----- 新增模板失败,错误: {e} -----------")  
            ethan_driver.getScreenshot("新增模板失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("--------- 测试用例 2 通过:新增模板成功 ---------------")  
  
    def search_template_test(self):  
        """测试用例 3: 搜索刚才新增的模板"""  
        print("\n--------- 开始测试用例 3 : 搜索模板功能 ---------------")  
        try:  
            self._return_to_template_page()  
  
            print(f"--- 正在搜索框中输入: {self.template_name}")  
            search_box = self.wait.until(EC.visibility_of_element_located(self.search_input))  
            search_box.clear()  
            search_box.send_keys(self.template_name)  
            ethan_driver.getScreenshot("输入搜索关键词")  
  
            print("--- 正在点击'搜模板'按钮...")  
            self.driver.find_element(*self.search_button).click()  
  
            # 直接刷新网页来等待搜索索引更新  
            print("--- 刷新页面以加载最新搜索结果...")  
            self.driver.refresh()  
            # 刷新后增加一个短暂的等待,确保页面元素重新加载完毕  
            time.sleep(2)  
  
            print("--- 正在验证搜索结果...")  
            searched_template_locator = (By.XPATH,  
                                         f"//div[@class='ant-card-meta-title']/a[text()='{self.template_name}']")  
            result_element = self.wait.until(EC.visibility_of_element_located(searched_template_locator))  
  
            assert self.template_name in result_element.text  
            print(f"--- 搜索成功,找到模板: {result_element.text}")  
            ethan_driver.getScreenshot("搜索成功")  
  
        except Exception as e:  
            print(f"----- 搜索模板失败,错误: {e} -----------")  
            ethan_driver.getScreenshot("搜索模板失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("--------- 测试用例 3 通过:搜索模板成功 ---------------")

页面:"个人设置" 测试类

自动化测试用例
自动化测试脚本
python 复制代码
import time  
from selenium.webdriver.common.by import By  
from selenium.webdriver.support.wait import WebDriverWait  
from selenium.webdriver.support import expected_conditions as EC  
  
# 导入浏览器驱动对象和登录函数  
from common.Utils import ethan_driver  
from tests.login_func import auto_login  
  
  
class ethan_personalTest():  
    def __init__(self):  
        self.driver = ethan_driver.driver  
        self.wait = WebDriverWait(self.driver, 10)  
        try:  
            print("--------- 准备登录并跳转到个人设置页面 ----------------------")  
            auto_login(self.driver, "admin", "123456")  
            target_url = "http://8.155.1.153:8081/system/setting"  
            self.driver.get(target_url)  
  
            # 验证跳转  
            check_get = (By.XPATH, "//div[contains(@class, 'right___')]//div[text()='基本设置']")  
            self.wait.until(EC.visibility_of_element_located(check_get))  
            print("----------- 登录并跳转成功 ----------------")  
  
        except Exception as e:  
            print(f"--------- 登录并跳转到这个个人页面设置 失败! 错误{e}-------------")  
            ethan_driver.getScreenshot("登录跳转到个人设置页面失败")  
            raise  
  
        # ------------------------- 页面元素定位 -----------------------        self.page_title = (By.XPATH, "//div[contains(@class, 'right___')]//div[text()='基本设置']")  
        # 姓名输入框  
        self.name_input_kuang = (By.CSS_SELECTOR, "#name")  
        # 手机号输入框  
        self.phone_number_input_kuang = (By.CSS_SELECTOR, "#phone")  
        # 邮箱输入框  
        self.email_input_kuang = (By.CSS_SELECTOR, "#email")  
        # 个人简介输入框  
        self.people_jie_shao_kuang = (By.CSS_SELECTOR, "#profile")  
        # 更新基本信息的【提交】按钮  
        self.update_button = (By.XPATH, "//button[.//span[normalize-space()='提 交']]")  
  
        # 用于验证成功的右上角用户名  
        self.header_username = (By.XPATH,  
                                "//header//span[contains(@class, 'ant-pro-global-header-header-actions-avatar')]//span[2]")  
        # 用于验证失败的提示信息  
        self.name_error_tip = (By.CSS_SELECTOR, "#name_help")  
        self.phone_error_tip = (By.CSS_SELECTOR, "#phone_help")  
        self.email_error_tip = (By.CSS_SELECTOR, "#email_help")  
  
    # ----------------------  执行测试用例 -------------------------------------------    def element_is_there(self):  
        """测试用例 1 : 检查页面相关元素是否存在"""  
        print("--------- 开始测试用例 1 : 检查页面相关元素是否存在---------------")  
        try:  
            print("---  正在验证: 姓名...")  
            self.wait.until(EC.visibility_of_element_located(self.name_input_kuang))  
            print("---  正在验证: 手机号...")  
            self.wait.until(EC.visibility_of_element_located(self.phone_number_input_kuang))  
            print("---  正在验证: 邮箱...")  
            self.wait.until(EC.visibility_of_element_located(self.email_input_kuang))  
            print("---  正在验证: 个人简介...")  
            self.wait.until(EC.visibility_of_element_located(self.people_jie_shao_kuang))  
            print("---  正在验证: 提交按钮...")  
            self.wait.until(EC.visibility_of_element_located(self.update_button))  
  
        except Exception as e:  
            print(f"这个页面缺少关键元素,错误:{e}")  
            ethan_driver.getScreenshot("个人设置缺少关键元素")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("----------- 测试用例 1 通过:页面关键元素都存在 ----------------")  
  
    def personal_success_test(self):  
        """测试用例 2: 正确更新个人信息"""  
        print("\n--------- 开始测试用例 2 : 正确更新个人信息 ---------------")  
        new_name = "改名测试"  
        try:  
            # 先清空所有输入框  
            print("--- 正在清空输入框...")  
            self.wait.until(EC.visibility_of_element_located(self.name_input_kuang)).clear()  
            self.driver.find_element(*self.phone_number_input_kuang).clear()  
            self.driver.find_element(*self.email_input_kuang).clear()  
            self.driver.find_element(*self.people_jie_shao_kuang).clear()  
            ethan_driver.getScreenshot("清空输入框后")  
  
            # 输入新信息  
            print(f"--- 正在输入新姓名: {new_name}")  
            self.driver.find_element(*self.name_input_kuang).send_keys(new_name)  
            print("--- 正在输入新手机号: 10000000000")  
            self.driver.find_element(*self.phone_number_input_kuang).send_keys("10000000000")  
            print("--- 正在输入新邮箱: 改邮箱测试@gmail.com")  
            self.driver.find_element(*self.email_input_kuang).send_keys("改邮箱测试@gmail.com")  
            print("--- 正在输入新简介: 啊,我真帅呀")  
            self.driver.find_element(*self.people_jie_shao_kuang).send_keys("啊,我真帅呀")  
            ethan_driver.getScreenshot("输入新信息后")  
  
            # 提交修改  
            print("--- 正在点击提交按钮...")  
            self.driver.find_element(*self.update_button).click()  
  
            # 等待成功提示出现  
            success_message = (By.XPATH, "//*[contains(text(), '更新成功')]")  
            self.wait.until(EC.visibility_of_element_located(success_message))  
            print("--- 看到了'更新成功'的提示!")  
            self.wait.until(EC.invisibility_of_element_located(success_message))  
  
            # 验证修改  
            print("--- 正在验证页面右上角用户名是否已更新...")  
            header_name_element = self.wait.until(EC.visibility_of_element_located(self.header_username))  
  
            assert header_name_element.text == new_name  
            print(f"--- 验证成功!右上角用户名已更新为: {header_name_element.text}")  
            ethan_driver.getScreenshot("修改个人信息成功")  
  
        except Exception as e:  
            print(f"------------ 修改个人信息失败 错误{e}-----------------")  
            ethan_driver.getScreenshot("修改个人信息失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("----------- 测试用例 2 通过:修改个人信息成功 ----------------")  
  
    def personal_fail_test(self):  
        """测试用例 3: 验证必填项为空时的错误提示"""  
        print("\n--------- 开始测试用例 3 : 验证必填项为空 ---------------")  
        try:  
            # --- 场景1: 姓名为空 ---            print("\n--- 场景1: 姓名为空 ---")  
            self.driver.refresh()  # 刷新页面以恢复初始状态  
            self.wait.until(EC.visibility_of_element_located(self.name_input_kuang)).clear()  
            self.driver.find_element(*self.update_button).click()  
            error_tip = self.wait.until(EC.visibility_of_element_located(self.name_error_tip))  
            assert "请输入您的昵称!" in error_tip.text  
            print(f"--- 验证成功,提示信息为: {error_tip.text}")  
            ethan_driver.getScreenshot("姓名为空时错误提示")  
  
            # --- 场景2: 手机号为空 ---            print("\n--- 场景2: 手机号为空 ---")  
            self.driver.refresh()  
            self.wait.until(EC.visibility_of_element_located(self.phone_number_input_kuang)).clear()  
            self.driver.find_element(*self.update_button).click()  
            error_tip = self.wait.until(EC.visibility_of_element_located(self.phone_error_tip))  
            assert "请输入您的联系电话!" in error_tip.text  
            print(f"--- 验证成功,提示信息为: {error_tip.text}")  
            ethan_driver.getScreenshot("手机号为空时错误提示")  
  
            # --- 场景3: 邮箱为空 ---            print("\n--- 场景3: 邮箱为空 ---")  
            self.driver.refresh()  
            self.wait.until(EC.visibility_of_element_located(self.email_input_kuang)).clear()  
            self.driver.find_element(*self.update_button).click()  
            error_tip = self.wait.until(EC.visibility_of_element_located(self.email_error_tip))  
            assert "请输入您的邮箱!" in error_tip.text  
            print(f"--- 验证成功,提示信息为: {error_tip.text}")  
            ethan_driver.getScreenshot("邮箱为空时错误提示")  
  
        except Exception as e:  
            print(f"------------ 反向流程测试失败 错误: {e}-----------------")  
            ethan_driver.getScreenshot("反向流程测试失败")  
            print("\n\n----------- 案发现场HTML代码 -----------")  
            print(self.driver.page_source)  
            print("--------------------------------------\n\n")  
            raise  
  
        print("----------- 测试用例 3 通过:反向流程验证成功 ----------------")

运行文件

python 复制代码
from common.Utils import ethan_driver  # 浏览器打开驱动类对象  
from tests.ethan_register import RegisterTest  # 导入注册页面测试类  
from tests.ethan_login import LoginTest  # 导入登录页面测试类  
from tests.ethan_home import homeTest  # 导入首页测试类  
from tests.ethan_project import ethan_projectTest  # 导入我的项目测试类  
from tests.ethan_question import ethan_question_Test  # 导入我的题库类  
from tests.ethan_template import ethan_templateTest  # 导入模版广场测试类  
from tests.ethan_personal import ethan_personalTest  # 导入个人设置测试类  
  
if __name__ == '__main__':  
    try:  
        # # ===================   注册模块测试  =========================        # print("############     开始执行注册模块测试     ##########")        # # 创建注册类的对象  
        # register_runner = RegisterTest()  
        # # 执行注册测试用例  
        # register_runner.RegisterSuccessTest()  
        # register_runner.register_fail_count()        # register_runner.register_fail_passwd()        # register_runner.register_fail_no_name()  
        # ####################    登录模块测试   ###################        # print("############     开始执行登录模块测试     ##########")        # # 创建登录类测试对象  
        # login_runner = LoginTest()  
        # # 执行登录测试用例  
        # login_runner.login_scuess()  
        # login_runner.login_error_name_or_passwd()        # login_runner.login_wrong_password()        # login_runner.login_no_username()        # login_runner.login_no_password()  
        # ####################    首页模块测试   ###################        # print("############     开始执行首页模块测试     ##########")        # # 创建首页测试的对象  
        # home_runner = homeTest()  
        # # 执行测试,验证元素是否存在  
        # home_runner.test_home_element()  
        # print("############     完成首页模块测试      ############")  
        # ####################    我的项目--模块测试   ###################        # print("############     开始执行:我的项目--模块测试    ##########")        # # 创建我的项目模块的测试对象  
        # project_runner = ethan_projectTest()  
        # # 执行测试用例  
        # project_runner.element_is_there()  
        # project_runner.create_wen_juan_test()        # project_runner.create_exam_test()        # print("############     完成:我的项目--模块测试      ############")  
        # ####################    我的题库--模块测试   ###################        # print("############     开始执行:我的题库--模块测试    ##########")        # # 创建我的题库类对象  
        # question_runner = ethan_question_Test()  
        # # 执行测试用例  
        # question_runner.element_is_there()  
        # question_runner.create_ti_ku()        # print("############     完成:我的题库--模块测试      ############")  
        # ####################    模板广场--模块测试   ###################        # print("############     开始执行:模板广场--模块测试    ##########")        # # 创建模板广场类对象  
        # template_runner = ethan_templateTest()  
        # # 测试用例 1 : 检查页面相关元素是否存在  
        # template_runner.element_is_there()  
        # # 测试用例 2 : 新增一个模板  
        # template_runner.create_template_test()  
        # # 测试用例 3 : 搜索刚才新增的模板  
        # template_runner.search_template_test()  
        # print("############     完成:模板广场--模块测试      ############")  
        ####################    个人设置--模块测试   ###################        print("############     开始执行:个人设置--模块测试    ##########")  
        # 创建个人设置类对象  
        personal_runner = ethan_personalTest()  
        # 执行测试用例  
        # 1. 元素在不在  
        personal_runner.element_is_there()  
        # 2. 正向流程,正确输入账号和密码  
        personal_runner.personal_success_test()  
        # 3. 反向/异常流程,必填项为空  
        personal_runner.personal_fail_test()  
        print("############     完成:个人设置--模块测试      ############")  
  
    # ========================  错误处理  =====================    except Exception as e:  
        print(f"\n!!!!!! 测试执行中断,错误: {e} !!!!!!")  
        ethan_driver.getScreenshot("unKnow_error")  
        raise  # 抛出异常,让系统知道测试失败了  
  
    # ========================  清理环境  ====================    finally:  
        # 无论成功失败,最后都关闭浏览器  
        ethan_driver.quit()  
        print("\n测试流程结束,浏览器已关闭。")

6.3 性能测试

目录结构:

相关配置

http 请求默认值
http 信息头管理器

6.3.1 单一接口性能测试

系统登录页面
登录接口
我的项目
我的练习
题库中心--我的题库
题库中心--问题管理
题库中心--我的笔记
模板广场
系统设置--用户管理
系统设置--角色管理
系统设置--组织机构
系统设置--岗位设置
系统设置--字典管理
系统设置--个人设置

6.3.2 性能测试报告

项目 描述
项目名称 问卷考试系统
测试类型 负载测试 (Load Test)
测试目标 评估系统在100并发用户负载下的性能表现,找出潜在瓶颈。
测试日期 2025年
报告作者 ethan
版本 V1.0
1. 测试概要
1.1. 测试环境与工具
  • 测试工具: Apache JMeter 5.5
  • 被测系统地址 : http://8.155.1.153:8081/
  • 核心插件 : JMeter Plugins Manager, jpgc - Standard Set
1.2. 测试场景设计

本次测试模拟了100个用户在5分钟内持续访问系统的核心业务流程。

  • 业务流程: 脚本覆盖了用户从登录到访问"我的项目"、"我的练习"、"题库中心"等一系列关键操作。
  • 负载设计 :
    • 并发用户数: 100 个线程
    • 启动时间 (Ramp-Up): 40 秒 (模拟用户平滑进入系统)
    • 持续时间: 300 秒 (5 分钟)
2. 测试结果与数据分析
2.1. 总体性能表现
关键指标 测试结果 结论分析
应用性能指数 (APDEX) 0.063 极低。分数范围为0-1,越接近1代表用户满意度越高。此分数表明,在100用户压力下,几乎所有用户的操作体验都处于"不可忍受"的"烦躁"状态。
请求成功率 100% 良好。在持续5分钟的压力下,系统没有出现任何由服务器导致的错误。这表明系统具备较高的稳定性,但在高压下处理能力不足。
总吞吐量 2.24 请求/秒 较低。与小压力(10用户)测试时相比,吞吐量显著下降,表明服务器处理能力已达到瓶颈并开始衰退。
平均响应时间 (总) 41,078 ms (41.1 秒) 极差。用户在系统中进行任意操作,平均需要等待超过40秒,这在实际应用中是无法接受的。
2.2. 核心瓶颈分析

通过对各请求的详细数据分析,我们定位到了导致系统整体性能低下的主要瓶颈。

请求名称 (Label) 平均响应时间 (ms) 90%用户响应时间 (ms) 吞吐量 (请求/秒) 问题分析
题库中心-问题管理 84,917 (84.9 秒) 147,806 (147.8 秒) 0.30 最严重瓶颈。平均等待时间近1.5分钟,90%的用户需要等待近2.5分钟才能看到页面,完全不可用。
我的项目 67,348 (67.3 秒) 130,554 (130.6 秒) 0.56 严重瓶颈。平均等待时间超过1分钟,是系统的第二大性能问题点。
我的练习 62,201 (62.2 秒) 121,637 (121.6 秒) 0.35 严重瓶颈。性能表现与"我的项目"类似,用户体验极差。
用户登录 2,500 (2.5 秒) 4,500 (4.5 秒) 1.50 相对较好,但在高压下也出现了明显性能下降,登录过程已非"瞬间完成"。

从初步的10用户测试的"响应时间变化图"中也已发现,高耗时操作的性能会随测试进行持续恶化,这表明相关功能可能存在资源无法及时释放或查询效率随数据量增加而急剧下降的问题。

3. 结论与建议
3.1. 结论

本次测试表明,问卷考试系统在100并发用户负载下:

  1. 稳定性尚可:系统能够持续运行不报错。
  2. 性能表现极差:系统响应速度极其缓慢,核心业务的平均响应时间普遍超过1分钟,远超用户可接受范围,存在严重的性能瓶颈,无法满足多用户同时使用的要求。
3.2. 优化建议

为提升用户体验并使系统达到可用状态,提出以下优化建议:

  • P0 (最高优先级) :
    • 立即优化 题库中心-问题管理 模块:建议开发人员重点排查该模块的数据查询语句、后端业务逻辑以及数据库索引,作为首要优化目标。
    • 立即优化 我的项目 模块:作为第二大瓶颈,建议紧随"问题管理"模块之后进行性能优化。
  • P1 (次高优先级) :
    • 建议对 我的练习我的笔记 等平均响应时间较长的模块进行代码审查和性能排查。
  • 后续步骤 :
    • 建议在开发团队完成上述优化后,执行一轮回归性能测试,以验证优化效果,确保系统性能得到实质性提升。

6.4 兼容性测试

测试场景一

在Chrome浏览器下进入问卷考试系统

预期结果:无异常且一切布局显示功能等与其一致

测试场景二

在 edge 浏览器下进入问卷考试系统

预期结果:无异常且一切布局显示功能等与其一致

测试场景三

在Firefox浏览器下进入问卷考试系统

预期结果:无异常且一切布局显示功能等与其一致

6.5 易用性测试

6.5.1 操作方面

考生从登录到完成考试、查看成绩的步骤清晰、高效、无困惑,如下图:

管理员创建考试/管理题库的操作直观 :

6.5.2 界面风格,导航一致

8. 项目测试 Bug 简述

8.1 我的题库

"导出"按钮异常:点击"导出"按钮无响应,导出功能失效

8.2 模版广场

"搜模板"按钮异常:输入已存在的"测试"点击"搜模板"按钮后,结果为空。

Bug ID 标题 严重级别 状态
BUG-001 "导出"按钮异常:点击"导出"按钮无响应,导出功能失效 未修复
BUG-002 "搜模板"按钮异常:输入已存在的"测试"点击"搜模板"按钮后,结果为空。 未修复

9. 测试结论

9.1 测试目标达成情况

本次测试按照原定测试计划执行,完成了以下测试活动:

  • 功能测试:覆盖了用户管理、问卷/考试管理、练习系统、题库管理、模板管理、系统设置等核心功能模块,验证了正向流程和关键异常场景。
  • 性能测试:使用 JMeter 模拟100并发用户持续访问核心业务5分钟,评估了系统在高负载下的表现。
  • 自动化测试:使用 Selenium + Python 实现了核心功能(用户注册/登录、首页元素验证、项目管理、模板管理、个人设置等)的自动化回归测试。
  • 兼容性测试:在 Chrome、Firefox、Edge 浏览器(Windows 11)上验证了系统核心功能。
  • 易用性测试 :评估了核心用户路径(如考生考试、管理员创建考试)的流畅性和界面友好性。
    结论:测试范围覆盖了计划内的所有内容,测试目标已达成。

9.2 测试执行情况

  • 功能测试:共设计并执行了162个测试用例,发现2个缺陷(报告中列出了2个Bug)。
  • 自动化测试:实现了核心流程的自动化脚本,共计34个测试用例。
  • 性能测试:在100并发用户下,系统虽然保持稳定(无错误),但核心接口响应时间严重超标(平均>40秒),存在明显性能瓶颈。
  • 兼容性测试:在目标浏览器上功能正常,布局显示一致。
  • 易用性测试:核心流程清晰,操作直观,但部分页面存在交互问题(如搜索功能异常)。

9.3 系统质量评估

  • 功能性:核心功能基本实现,但存在个别功能缺陷(如模板搜索失效、导出功能异常)。
  • 性能:系统在100并发用户下响应极慢(主要接口平均响应时间>40秒),无法满足实际生产需求,是当前最严重的质量问题。
  • 兼容性:良好,在目标浏览器上表现一致。
  • 易用性:整体良好,核心路径流畅,但部分功能(如搜索)的异常会影响用户体验。
  • 稳定性 :在压力测试中未出现崩溃或服务中断,表现稳定。
    总体评价:系统基本功能可用,但性能问题突出,且存在关键功能缺陷(BUG-002),目前质量未达到发布标准。

9.4 主要风险与问题

  1. 性能瓶颈:高并发下响应时间极长,用户体验不可接受(APDEX指数仅0.063)。需重点优化"题库管理"、"我的项目"等模块。
  2. 功能缺陷
  • BUG-002:"搜模板"功能失效,影响用户快速查找模板。
  • BUG-001:"导出"功能失效,影响题库导出需求。
  1. 自动化测试覆盖率:当前自动化测试覆盖了核心正向流程,但异常场景和部分模块(如练习系统)尚未覆盖。

9.5后续行动建议

  1. 优先解决性能瓶颈:优化"题库中心-问题管理"、"我的项目"等接口的响应时间。
  2. 修复高优先级Bug:立即修复 BUG-002 和 BUG-001。
  3. 回归测试:修复后重新执行性能测试和功能回归测试(包括自动化回归)。
  4. 补充测试覆盖:增加练习系统、错题管理等模块的自动化测试用例。