1. 项目概述:从课程设计到实战演练
最近在带学生做软件测试的课程设计,发现很多同学拿到“学之思考试系统”这个项目时,虽然知道要用Postman和Selenium,但具体怎么把这两者结合成一个有深度、能体现测试思维的“大作业”,往往无从下手。要么是Postman随便发几个请求,要么是Selenium录几个简单的点击操作,报告写得干巴巴,完全体现不出测试工程师应有的系统化思考。今天,我就以一个资深测试从业者的视角,来拆解一下如何围绕“学之思考试系统”,完成一份高质量的、包含接口测试与Web自动化测试的课程设计报告。这不仅仅是交作业,更是你未来面试时一个拿得出手的实战项目。
“学之思”是一个开源的在线考试系统,功能涵盖了从题库管理、试卷生成、在线考试到自动阅卷、成绩分析的全流程。我们的测试目标,就是要验证这套系统核心业务流程的可靠性与稳定性。为什么选择Postman+Selenium这个组合?这背后有清晰的测试分层思想:Postman负责后端API接口的验证,这是数据与业务逻辑的基石;Selenium则负责前端用户界面的交互与流程验证,确保用户能顺畅地完成操作。两者结合,恰好覆盖了从服务端到客户端的完整测试链条。这份报告的核心价值,就在于展示你如何系统地设计测试用例、执行测试并分析结果,而不仅仅是工具的使用。
2. 测试策略与整体方案设计
一份优秀的测试报告,开篇必须讲清楚“测什么”和“怎么测”。对于学之思考试系统,我们不能漫无目的地乱测,需要先进行需求分析与测试范围界定。
2.1 核心业务流程分析与测试范围界定
首先,你需要深入理解学之思系统的核心业务流。一个典型的考试流程包括:管理员登录 -> 创建题库与试题 -> 组卷并发布考试 -> 学生登录 -> 参加考试并提交 -> 系统自动阅卷 -> 查看成绩与分析。我们的测试重点就应该围绕这条主干道展开,确保核心功能畅通无阻。
基于此,我建议将测试范围划分为两大块:
- API接口测试(Postman):聚焦于后端服务的健壮性。重点测试用户认证、试题/试卷的增删改查、考试提交与成绩生成等关键接口。这部分测试不依赖界面,速度快,能快速发现底层逻辑错误。
- Web UI自动化测试(Selenium):聚焦于前端交互与端到端流程。模拟真实用户行为,测试完整的考试流程,例如学生从登录到完成考试的整个过程,以及管理员后台的各项操作是否顺畅。
测试环境搭建是第一步。你需要一个稳定的测试环境。建议使用Docker快速部署一套学之思系统的测试环境,与开发/生产环境隔离。数据库准备一份干净的、包含基础数据(如测试账号、简单试题)的备份,每次测试前恢复,保证测试起点一致。这是做出可重复、可靠测试的基础,很多同学忽略了环境一致性,导致测试结果时好时坏,无法定位问题。
2.2 测试工具选型与框架设计思路
为什么是Postman和Selenium?这不仅仅是课程要求,更是行业实践。
- Postman:几乎是API测试的代名词。它图形化界面友好,适合新手快速上手,同时也支持通过Collection组织用例、Environment管理环境变量、编写Pre-request Script和Tests进行自动化断言,完全可以满足课程设计对接口测试的深度要求。对于需要免登录或汉化的场景,网上有成熟的解决方案,但核心在于理解其变量、断言和集合运行机制。
- Selenium:Web UI自动化的标准。我们选择Selenium WebDriver配合Python语言。Python语法简洁,生态丰富,是自动化测试的主流选择之一。我们需要构建一个简单的、但结构清晰的自动化测试框架。这个框架不必像企业级那么复杂,但至少要体现分层思想:比如将页面元素定位信息(Locators)单独管理,将常用的操作(如登录、查找元素)封装成公共函数,将测试用例(Test Cases)与测试数据(Test Data)分离。
一个简单的框架目录结构可以这样设计:
project/ ├── common/ │ ├── __init__.py │ ├── base_page.py # 封装基础页面操作类 │ └── logger.py # 日志记录模块 ├── page_objects/ │ ├── __init__.py │ ├── login_page.py # 登录页面元素和操作 │ └── exam_page.py # 考试页面元素和操作 ├── test_cases/ │ ├── __init__.py │ └── test_student_take_exam.py # 学生考试流程测试用例 ├── test_data/ │ └── config.ini # 配置文件(URL、账号等) ├── reports/ # 存放测试报告 └── run_tests.py # 主运行脚本这样的设计,能让你的代码看起来更专业,也便于后续维护和扩展。记住,框架的目的是提高效率和可维护性,而不是为了复杂而复杂。
3. Postman接口测试实战与深度解析
接口测试是验证系统“内在美”的关键。对于学之思系统,我们需要系统地对其RESTful API进行测试。
3.1 接口梳理、用例设计与Collection组织
首先,通过查阅学之思系统的API文档(如果文档不全,可能需要配合浏览器开发者工具的Network面板进行分析),梳理出核心接口。例如:
- 认证类:
POST /api/login - 题库管理:
GET /api/question/list,POST /api/question/create,PUT /api/question/update - 考试过程:
GET /api/exam/start,POST /api/exam/submit - 成绩查询:
GET /api/score/{examId}
在Postman中,我们创建一个名为“学之思考试系统_API测试”的Collection。然后为每个接口创建Request(请求)。这里的关键在于参数化和自动化断言。
以登录接口POST /api/login为例:
- 请求配置:Method设为POST,URL填写
{{base_url}}/api/login。这里的{{base_url}}是一个环境变量,方便在不同环境(测试、生产)间切换。 - 请求体:选择
raw格式和JSON,填写如{"username": "{{student_username}}", "password": "{{student_password}}"}。账号密码也使用环境变量,避免硬编码。 - Pre-request Script:可以在这里生成一些动态数据,比如时间戳。但对于登录,通常不需要。
- Tests:这是精华所在。在这里编写JavaScript代码来验证响应。例如:
通过这样的断言,我们不仅检查了功能(登录成功),还检查了性能(响应时间)和数据完整性(返回了token)。// 检查状态码为200 pm.test("Status code is 200", function () { pm.response.to.have.status(200); }); // 检查响应体包含token pm.test("Response has auth token", function () { var jsonData = pm.response.json(); pm.expect(jsonData.token).to.be.a('string').that.is.not.empty; // 将获取到的token设置为环境变量,供后续接口使用 pm.environment.set("auth_token", jsonData.token); }); // 检查响应时间在合理范围内 pm.test("Response time is less than 500ms", function () { pm.expect(pm.response.responseTime).to.be.below(500); });
3.2 环境变量、前置操作与测试数据管理
高效地管理测试数据是接口自动化的核心。Postman的Environment功能至关重要。你可以创建“Test Environment”和“Production Environment”,分别配置不同的base_url、用户账号等。在请求中通过{{variable_name}}引用。
对于有依赖关系的接口流程,比如“创建试题 -> 查询试题列表验证”,可以使用Postman的Collection Runner或Newman(命令行工具)来顺序执行。关键在于,将上一个接口的响应结果,提取为变量供下一个接口使用。除了上面在Tests里用pm.environment.set()设置环境变量,还可以使用Pre-request Script为某些接口生成动态的测试数据,比如用Math.random()生成不重复的试题标题,避免因数据重复导致创建失败。
注意:对于登录态(token)的传递,大部分接口需要在Header中添加
Authorization: Bearer {{auth_token}}。你可以在Collection层级设置一个Auth预设,或者在每个请求的Headers中手动添加。建议在Collection层级设置,这样该Collection下的所有请求都会自动继承,除非某个请求(如登录本身)需要覆盖。
3.3 边界值、异常场景与安全测试考虑
一个完整的接口测试不能只测“happy path”。你的课程设计要体现出测试思维的全面性。
- 边界值测试:对于分页查询接口(如
/api/question/list?page=1&size=10),要测试size为0、1、最大值、超过最大值的情况。 - 异常测试:
- 登录接口:输入错误的密码、不存在的用户名、空用户名密码。
- 创建试题接口:请求体JSON格式错误、必填字段缺失、字段类型错误(如数字传字符串)、字段长度超限。
- 权限测试:用学生token去尝试调用管理员创建试卷的接口,应返回403 Forbidden。
- 安全测试浅尝:可以尝试对登录接口进行简单的SQL注入测试(如用户名输入
admin' --),虽然学之思作为成熟开源项目大概率已防护,但这是一个重要的测试点。也可以检查返回的响应头是否包含敏感信息(如服务器版本号),或者token是否在HTTP响应中明文传输(应只在HTTPS下)。
将这些异常用例也设计到你的Postman Collection中,并为它们编写相应的断言,例如期望状态码是400(错误请求)或401(未授权)。这能极大地丰富你报告中的“测试用例设计”部分。
4. Selenium Web自动化测试框架搭建与核心实现
Web自动化测试模拟的是真实用户,它能发现那些接口测试发现不了的、前后端交互或页面渲染导致的问题。
4.1 环境搭建、元素定位策略与Page Object模式实践
首先,搭建Python+Selenium环境。使用pip安装:pip install selenium webdriver-manager。webdriver-manager这个库非常好用,它能自动下载和管理不同浏览器的驱动,省去了手动配置的麻烦。
启动浏览器并访问学之思系统的Web地址:
from selenium import webdriver from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service # 使用 webdriver-manager 自动管理Chrome驱动 service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service) driver.get("http://your-test-system.com") driver.maximize_window()元素定位是自动化测试的基石,也是最容易出问题的地方。优先选择ID和Name,因为它们通常唯一且稳定。其次是CSS Selector和XPath。XPath功能强大但性能稍差,且容易因页面结构微小变动而失效。建议在浏览器开发者工具中使用$x()或$$()进行快速测试。
为了提高代码的可维护性和复用性,必须采用Page Object Model设计模式。简单说,就是为每个网页(或页面关键区域)创建一个类,这个类内部封装了该页面的所有元素定位器和页面操作方法。测试用例类则通过调用这些页面对象的方法来完成操作。
例如,创建一个LoginPage类:
class LoginPage: def __init__(self, driver): self.driver = driver self.username_input = (By.ID, "username") # 将定位器定义为元组 self.password_input = (By.ID, "password") self.submit_button = (By.XPATH, "//button[@type='submit']") self.error_message = (By.CLASS_NAME, "alert-error") def enter_username(self, username): self.driver.find_element(*self.username_input).send_keys(username) def enter_password(self, password): self.driver.find_element(*self.password_input).send_keys(password) def click_submit(self): self.driver.find_element(*self.submit_button).click() def get_error_text(self): return self.driver.find_element(*self.error_message).text def login(self, username, password): self.enter_username(username) self.enter_password(password) self.click_submit()在测试用例中,使用方式就非常清晰:
def test_student_login_success(self): login_page = LoginPage(self.driver) login_page.login("student1", "123456") # 断言登录成功,例如检查页面是否跳转到 dashboard assert "我的考试" in self.driver.title4.2 核心业务流程自动化脚本编写
我们以“学生完成一次考试”这个核心端到端流程为例,编写自动化脚本。这个过程会涉及多个页面:登录页、考试列表页、考试答题页、提交确认页。
思路如下:
- 学生登录。
- 在“我的考试”或“进行中的考试”列表中,找到指定的考试并点击“开始考试”。
- 进入答题页面,遍历所有试题,根据题型(单选、多选、填空)模拟答题。
- 对于选择题:随机选择一个选项(或选择特定答案)。
- 对于填空题:在输入框中输入预设的答案文本。
- 关键点:注意页面可能存在“下一题”按钮,或者是一次性展示所有题目。需要根据实际UI结构来设计操作逻辑。
- 所有题目答完后,点击“提交试卷”。
- 处理可能的确认弹窗。
- 跳转到结果页面,断言是否显示“提交成功”或成绩信息。
在编写脚本时,必须处理等待问题。页面加载、元素出现都需要时间。绝对不要使用time.sleep(固定秒数),这是不稳定和低效的根源。要使用Selenium提供的显式等待:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待某个元素出现并可点击 start_exam_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "开始考试")) ) start_exam_btn.click() # 等待页面标题包含特定文字 WebDriverWait(driver, 10).until( EC.title_contains("考试答题") )显式等待会在指定时间内(如10秒)不断尝试,一旦条件满足就立即继续,否则超时抛出异常。这能大大提高测试的稳定性和执行速度。
4.3 测试数据驱动、断言与报告生成
为了让测试更灵活,我们需要将测试数据从脚本中分离。可以使用Python的unittest或pytest框架的参数化功能,或者简单地从JSON/Excel/YAML文件中读取数据。
例如,使用pytest的参数化:
import pytest test_login_data = [ ("student1", "123456", True), # 正确账号,期望成功 ("wrong_user", "123456", False), # 错误账号,期望失败 ("student1", "wrong_pwd", False), # 错误密码,期望失败 ] @pytest.mark.parametrize("username, password, expected_success", test_login_data) def test_login(self, username, password, expected_success): login_page = LoginPage(self.driver) login_page.login(username, password) if expected_success: assert "我的考试" in self.driver.title else: assert "用户名或密码错误" in login_page.get_error_text()断言是验证测试结果的手段。除了使用Python自带的assert,可以结合Selenium的WebDriverWait和expected_conditions进行更丰富的断言,如检查元素是否存在、是否可见、文本内容是否匹配等。
最后,生成一份美观的测试报告。可以使用pytest-html插件,在运行测试时自动生成HTML报告:
pytest test_cases/ --html=reports/report.html --self-contained-html这份报告会详细列出每条用例的执行结果(通过/失败)、耗时、错误日志和截图(如果配置了失败自动截图),非常适合放入课程设计报告中作为成果展示。
5. 测试执行、问题分析与报告撰写心法
工具和脚本都准备好了,如何执行并得到有价值的结论,是区分普通作业和优秀作业的关键。
5.1 测试计划执行与结果记录
制定一个简单的测试执行计划。例如:
- 冒烟测试:用Postman快速运行核心接口(登录、获取试卷列表),用Selenium执行学生登录并查看考试列表。确保系统基本可用。
- 功能测试:全面执行设计好的Postman Collection和Selenium测试套件。
- 回归测试:如果开发修复了Bug,针对修复的功能点及相关联模块进行重点测试。
执行过程中,要做好详尽的记录。Postman Runner或Newman会生成JSON格式的测试报告。Selenium配合pytest-html会生成HTML报告。你需要人工去审查这些报告,特别是失败的用例。记录下:
- 失败用例的名称和描述。
- 失败时的现象(错误截图非常重要!)。
- 失败发生的环境、步骤和数据。
- 初步分析的错误原因。
5.2 典型Bug分析、定位与报告编写
在测试中,你可能会发现各种问题。在报告中挑选2-3个有代表性的Bug进行深入分析,这能极大提升报告的深度。例如:
Bug示例1:接口参数校验缺失
- 现象:通过Postman调用创建试题接口,将“分值”字段传入一个负数(如-5),接口成功返回,试题被创建。
- 分析:这属于后端接口参数校验不完整。虽然前端可能做了限制,但直接调用接口可以绕过。负分值在业务逻辑上是无意义的,可能导致后续计算总分时出现错误。
- 定位:通过查看接口响应或数据库,确认试题分值确实被存为负数。这是一个服务端校验缺失的Bug。
- 报告建议:在Bug报告中,应明确标题“【后端】创建试题接口未对分值进行非负校验”,并提供请求报文、响应报文以及数据库记录截图。
Bug示例2:前端竞态条件导致状态异常
- 现象:在Selenium自动化测试“快速连续点击提交试卷按钮”时,偶尔会出现重复提交,生成两条相同的提交记录。
- 分析:这可能是前端没有在点击提交后及时禁用按钮,导致在网络请求未返回前,用户(或自动化脚本)可以再次点击。后端如果没有做幂等性处理,就会产生重复数据。
- 定位:通过Selenium脚本复现,并查看浏览器Network面板,确认发出了两次相同的POST请求。这是一个前端交互与后端逻辑结合部的Bug。
- 报告建议:标题可为“【前端/后端】提交试卷按钮可重复点击导致重复提交”。建议前端在点击后禁用按钮直至请求返回,后端对同一用户同一试卷的提交请求做幂等性校验。
在课程设计报告中,你可以用表格形式汇总发现的Bug:
| Bug ID | 模块 | 标题 | 严重等级 | 复现步骤 | 预期结果 | 实际结果 | 原因分析 |
|---|---|---|---|---|---|---|---|
| 1 | 试题管理 | 创建试题接口接受负分值 | 中 | 1. 调用创建试题API... | 应返回参数错误 | 接口成功,创建了负分值试题 | 后端缺少参数范围校验 |
| 2 | 考试提交 | 快速点击提交按钮可重复提交 | 高 | 1. 学生进入考试... | 应只提交一次 | 生成了两次提交记录 | 前端未防重,后端未做幂等 |
5.3 测试总结、效能评估与改进建议
这是报告的画龙点睛之笔。不要只说“我用Postman和Selenium完成了测试”。要总结测试覆盖度、缺陷分析、自动化收益和个人反思。
- 测试覆盖度评估:统计你设计的接口用例数、UI自动化用例数,并说明它们覆盖了哪些核心功能模块(如用户管理、试题管理、考试流程、成绩查询)。可以画一个简单的功能模块与用例覆盖的矩阵图。
- 缺陷分析:总结共发现了多少个Bug,按严重程度(致命、严重、一般、轻微)和模块分布进行分类。分析这些Bug主要产生在哪个阶段(接口逻辑、前端交互、数据校验等)。这能体现你的测试洞察力。
- 自动化效能评估:对比一下。手动执行一遍完整的核心考试流程可能需要10分钟,而你的Selenium脚本可能只需要2分钟。Postman Collection可以一键运行所有接口测试。量化自动化带来的时间节省。同时,也要客观指出自动化测试的局限性,比如对UI频繁变化的维护成本较高,无法替代探索性测试和用户体验测试。
- 改进建议:基于你的测试实践,提出对“学之思考试系统”本身和自身测试方案的改进建议。
- 对系统:建议加强后端接口的输入校验;建议为关键操作(如提交)增加防重机制;建议提供更丰富的API响应状态码以便测试断言。
- 对测试方案:本次由于时间所限,未进行性能测试(如多人同时考试)。后续可以使用JMeter对系统进行压力测试。自动化测试框架可以引入Allure生成更美观的测试报告,并集成到CI/CD流程中(如Jenkins),实现每日构建后的自动回归测试。
最后,在报告附录中,附上你的Postman Collection导出文件(JSON)、核心Selenium脚本代码片段以及生成的测试报告截图。一份有数据、有分析、有代码、有结论的课程设计报告,必然能让你在众多作业中脱颖而出。记住,工具只是手段,通过测试保障质量、发现风险的思维才是核心价值。