资讯动态

软件测试基础与Python接口自动化:登录项目实战详解

发布时间:2026/9/4 15:51:58 来源:尧图企业网站定制
不少刚开始学习软件测试的同学都会有一个相似的困惑测试工作不就是照着用例在页面上“点点点”吗为什么面试会问测试流程、用例设计还要会 Python甚至会问 while 循环这种编程基础其实软件测试并不是“会点击”就能长期做好的岗位。随着项目复杂度提升测试人员需要同时具备两层能力第一层是测试基础比如需求分析、用例设计、缺陷管理和测试流程第二层是编程能力比如写接口自动化脚本、做数据构造、处理异步任务轮询。而 while 循环恰好是测试脚本里非常常用的基础语法只是很多人学的时候没想明白它到底用在哪里。这篇文章把两条线合并在一起从软件测试基础概念讲起再到测试过程和用例设计方法然后通过一个本地可运行的登录接口项目串起完整的功能测试与接口自动化测试流程并在自动化脚本中重点演示 while 循环在“轮询等待、重试、批量重复操作”里的实际用法。无论你是正准备入行的零基础新人还是已经工作一段时间想补齐自动化测试技能的初级测试工程师都可以按文章顺序自己动手跑一遍。1. 软件测试基础测试到底解决什么问题1.1 什么是软件测试软件测试的定义比较容易背但要理解它背后的目标并不容易。软件测试是指在规定的条件下对软件系统进行操作以发现程序中的错误衡量软件质量并评估其是否满足需求的过程。这里面有三个关键词值得注意规定的条件不是随便乱点而是基于需求、规格说明、测试用例来执行。发现程序中的错误测试要尽可能找出 Bug而不是只证明“软件能跑”。是否满足需求哪怕 Bug 很少如果功能不符合业务需求产品依然不能用所以测试关注的是“需求符合度”。用大白话理解软件测试不是“证明没有 Bug”而是“在有限的时间和资源内尽可能暴露风险让团队在发布前知道当前版本的质量水平”。这种“验证 确认”的思维方式会贯穿整个测试生涯。1.2 软件测试的分类软件测试分类有很多维度。如果面试或工作里听别人说“我们组做系统测试”“现在开始接口测试”“这轮做回归”说的往往是不同分类方式下的概念。下面这张表把常见的划分方式整理出来划分维度类型说明按开发阶段单元测试对最小代码单元进行验证通常由开发完成按开发阶段集成测试验证模块与模块之间的接口与数据传递按开发阶段系统测试对完整系统进行功能和业务场景验证按开发阶段验收测试确认系统是否满足业务需求是否可上线按是否运行程序静态测试不运行代码通过代码评审、文档审查发现问题按是否运行程序动态测试运行程序并输入数据观察输出结果按执行方式手工测试测试人员手动执行用例适合探索性和 UI 类场景按执行方式自动化测试通过脚本或工具执行用例适合回归和重复操作按测试目标功能测试验证功能是否正确按测试目标性能测试验证系统响应时间、吞吐量、稳定性按测试目标兼容性测试验证不同浏览器、设备、系统版本下的表现按测试目标安全测试验证系统防护能力与数据安全对于零基础入门的人来说不需要第一天就把所有类型的细节背熟但至少要能在实际工作中听懂这些词。比如你做的是 Web 项目核心一般是系统功能测试和接口自动化测试如果是基础平台项目开发和测试都会更关注单元测试覆盖率。1.3 测试金字塔给入门者的启示测试金字塔是测试领域很经典的分层模型简单说就是越往底层测试数量越多、执行速度越快、维护成本越低越往顶层测试数量越少、执行速度越慢、维护成本越高。实际项目中的合理分布通常是底层是单元测试数量多由开发人员负责。中间层是接口测试数量较多质量收益高是自动化测试的重点。顶层是 UI 端到端测试数量少只覆盖核心主流程。很多刚入门的朋友容易陷入“自动化测试 UI 自动化 Selenium”的误区一上来就花大量时间写 UI 脚本。但从测试金字塔来看接口层自动化通常比 UI 自动化更稳定、也更容易维护因为它不依赖页面元素定位不会因为某个按钮的 CSS 样式变化而大面积失败。这也是本文项目实战选择接口测试而不是 UI 测试的原因。学会接口测试再回头看 UI 自动化会清楚很多。2. 从测试流程到用例设计测试基础核心内容2.1 标准测试流程无论小项目还是大项目测试都不会是“拿到版本直接开点”。一个完整的测试过程通常包含下面几个阶段第一阶段需求分析和测试计划测试人员从需求评审阶段就要介入而不是等开发把功能做完才开始。在这个阶段要弄清楚几个问题这次改动涉及哪些功能核心用户是谁业务规则是什么哪些场景最容易出问题有没有历史遗留 Bug 需要回归在分析清楚需求之后测试负责人会制定测试计划明确测试范围、资源、时间、环境、风险等。即使你暂时不是负责人也要学会给自己列一个简单的测试安排比如先测主流程、再测异常场景、最后做回归。第二阶段测试用例设计这是测试基础里非常重要的能力。同一个功能会设计用例的人能覆盖正常流程和各种异常分支不会设计的人只能测到“输入正确账号密码能不能登录”。测试用例设计方法和用例管理是面试中被问频率很高的内容。第三阶段测试执行按照用例在指定测试环境执行记录实际结果。执行过程中一旦发现预期结果不一致需要先定位是否环境问题、数据问题还是代码缺陷。确认是代码缺陷后提交缺陷报告。第四阶段缺陷管理与回归测试人员提交 Bug 后要和开发保持沟通及时验证修复结果。开发修复一个新 Bug 后除了验证该 Bug还要对相关功能做回归测试防止“修一个 Bug 引出三个新 Bug”。第五阶段测试报告与上线评估版本准备发布前测试负责人需要输出测试报告内容包括用例执行情况、遗留缺陷、风险点和结论。测试通过不代表没有风险而是风险已经到了团队可接受的范围。以上流程看起来简单但实际项目执行中经常会出现需求变更频繁、发版时间固定、测试时间严重不足的情况。这时候更考验测试人员对核心用例的把握能力以及是否建立了可复用的自动化回归资产。2.2 测试用例设计方法等价类、边界值、场景法测试用例设计方法有很多入门阶段最常用的是下面三种。等价类划分等价类划分的思路是不需要对每个输入值单独测而是把输入数据分成若干个“等价的类”同一类中的数据对程序处理逻辑来说是一样的。以登录功能为例假设用户名规则是6 到 12 位字母或数字。那么可以划分出有效等价类6 位以上、12 位以下的字母数字组合。无效等价类小于 6 位、大于 12 位、包含特殊字符、为空等。从每个等价类中取一个代表值进行测试就能用较少的用例覆盖较多场景。边界值分析边界值是基于“程序错误经常发生在边界附近”这一经验。比如用户名长度限制是 6 到 12 位那么需要重点测试的是 5 位、6 位、12 位、13 位这些值最容易触发长度判断错误。边界值分析通常和等价类一起使用先用等价类确定测试范围再用边界值补充边界场景。场景法场景法主要针对用户真实使用流程通过“基本流 备选流”来设计用例。还是以登录为例基本流是输入正确用户名和密码点击登录进入系统首页。备选流包括密码错误一次、连续错误达到锁定阈值、用户被禁用、网络超时等。下面是一个简单的登录功能测试用例示例这里不绑定具体系统目的是展示一条用例应该包含什么用例编号测试标题前置条件测试步骤输入数据预期结果TC-LOGIN-001登录成功用户已注册且状态正常打开登录页输入正确信息点击登录test01 / 123456登录成功跳转首页TC-LOGIN-002密码错误用户已注册输入正确用户名错误密码点击登录test01 / 000000提示用户名或密码错误TC-LOGIN-003连续失败 5 次锁定用户已注册连续输入 5 次错误密码test01 / 000000第 5 次提示账号锁定TC-LOGIN-004用户名为空登录页打开密码填正确用户名不填空 / 123456提示用户名不能为空这条用例里的 TC-LOGIN-003 会在后面的项目实战中对应到具体自动化代码你可以结合项目再看一遍。2.3 缺陷报告怎么写发现 Bug 只是第一步把 Bug 描述清楚同样重要。一个让开发能快速理解和复现的缺陷报告最好包含下面几个字段缺陷标题一句话概括问题现象例如“密码连续错误 5 次后第 6 次输入正确密码仍提示锁定”。所属模块登录、注册、订单等。前置条件测试环境、测试账号、测试数据。复现步骤按顺序写出操作步骤步骤要能完整复现。实际结果执行后系统真实表现。预期结果根据需求系统应该表现成什么样。严重程度致命、严重、一般、轻微。致命指系统崩溃、数据丢失严重指主流程不可用一般指非主流程功能受影响轻微指界面展示、文案等问题。优先级紧急、高、中、低。优先级通常和严重程度正相关但还要看对用户影响范围。附件截图、日志、报文等。很多新手提交缺陷时只写“登录失败”这样开发很难定位到底是什么环节、什么条件下失败。一份好的缺陷报告本质上是把“开发复现问题和修复问题的成本”降到最低。3. 测试脚本编程基础为什么要学 Python 和 while 循环3.1 测试里真的需要写代码吗这是一个很实际的问题。如果只做功能测试可能短期内确实不需要写复杂代码。但一旦涉及接口自动化、测试数据准备、日志分析、CI 集成就离不开脚本能力。自动化测试的核心是“让程序代替人做重复的事情”而这恰恰需要你理解和编写逻辑。比如“等待某个异步任务完成”“请求失败后重试”“循环执行一组测试数据”等场景如果不会编程只能用笨办法一遍遍手工复制或者写一堆重复且低效的 sleep。在众多语言里Python 是测试领域最常用的选择之一。它语法简洁、第三方库丰富requests、pytest、selenium、allure 等生态都很成熟。对测试工程师来说建议从 Python 基础语法开始学其中 while 循环又是绕不开的知识点。3.2 while 循环基本语法while 循环的作用是当指定条件为真时重复执行某段代码直到条件变成假。最基本的结构如下count 0 while count 5: print(f当前第 {count} 次循环) count 1运行结果当前第 0 次循环 当前第 1 次循环 当前第 2 次循环 当前第 3 次循环 当前第 4 次循环这里要注意count 1这一行。while 循环本身不会自动让条件发生变化如果没有count 1count永远小于 5程序就会进入死循环一直在循环体里执行。还有一种常用的写法是while True表示条件永远为真配合break语句在满足某个条件时主动退出循环num 0 while True: num 1 if num 3: break print(num)运行结果3这种“先无条件进入循环再判断是否跳出”的写法在实际测试脚本中非常实用尤其是在等待外部系统返回结果的场景里。3.3 测试工作中最常见的三个 while 循环场景场景一轮询等待异步任务完成接口测试中经常遇到异步接口。比如提交一个任务之后服务端需要几秒甚至几十秒处理测试脚本不能立刻断言结果而是需要每隔一段时间查询一次任务状态直到成功或者超时。这种“轮询等待”如果用 while 循环实现会非常清晰。一个通用轮询模板如下import time timeout 30 # 最大等待时间 interval 2 # 每次查询间隔 start_time time.time() while True: status get_task_status(task_id) # 查询任务状态 if status SUCCESS: print(任务执行成功) break if time.time() - start_time timeout: raise TimeoutError(等待任务执行超时) time.sleep(interval)这个代码的关键点在于给 while 循环设置了一个超时出口。否则一旦服务端状态一直不返回脚本就会无限等待下去导致测试卡死。场景二请求失败自动重试测试脚本访问接口时可能因为网络抖动或服务重启导致偶发失败。如果第一次请求失败就直接断言失败很容易产生误报。更合理的做法是设置重试次数让脚本自动再请求几次。示例如下import time import requests max_retry 3 retry_count 0 while retry_count max_retry: try: response requests.get(http://127.0.0.1:5000/api/users, params{username: admin}, timeout5) if response.status_code 200: print(请求成功) break except requests.RequestException as exc: print(f请求异常{exc}) retry_count 1 time.sleep(1) if retry_count max_retry: raise RuntimeError(请求多次重试仍然失败)这里break表示成功后就退出循环retry_count 1控制重试次数。要注意重试并不是无脑把所有失败都重试。如果是参数错误、无权限这类确定性问题重试没有任何意义。通常只对超时、连接异常、5xx 类临时错误进行重试。场景三重复执行固定次数的测试操作有些用例需要重复执行多次比如连续 5 次错误密码来验证锁定逻辑。虽然用 for 循环可能更简洁但 while 循环在“中间需要做状态判断”时一样常用甚至更直观wrong_count 0 while wrong_count 5: send_login_request(username, wrong_password) wrong_count 1你可能会想这种固定次数循环用for i in range(5)不是更好吗确实如果只是“执行 5 次”那么简单for 循环更推荐。但很多实际场景是在循环体里需要根据当前阶段判断走不同分支这时 while 加上显式的计数器逻辑可读性会更好。两种写法都该掌握面试时能对比说清楚区别会更容易加分。3.4 while 循环的易错点在测试脚本中while 循环用错了通常不是语法报错而是逻辑上的“死循环”或“少循环一次”。比较常见的坑有下面几个。第一忘记更新循环条件。比如上面计数器示例中少了count 1程序永远不会退出。写代码时要先想清楚“这个循环靠什么条件退出”。第二超时条件写错或漏写。轮询等待场景里如果只写了while True又没写超时判断一旦外部接口异常脚本就会无限执行下去。所以项目实践中凡是while True基本都要配套超时逻辑。第三break 和 continue 弄混。break是终止整层循环continue是跳过当前这一次继续执行下一轮。在重试逻辑中写错continue可能导致重试次数根本没有增加形成死循环。第四循环内 sleep 时间设置不合理。如果每隔 0.1 秒就请求一次接口可能给被测服务造成压力也不符合真实场景。一般建议轮询间隔设置在 0.5 秒到 2 秒之间具体根据业务响应速度调整。4. 项目实战登录接口从需求到自动化测试4.1 项目需求说明为了真正把测试基础和 while 循环串起来这里准备一个本地可运行的模拟登录服务。它模拟了一个比较常见的后端系统包含三个接口接口方法功能注意事项/api/users/createPOST创建测试用户模拟异步创建3 秒后用户状态才变为 active/api/usersGET查询用户状态用于测试脚本轮询等待/api/loginPOST用户登录包含错误次数统计和锁定逻辑被测服务逻辑如下用户第一次创建时状态为 creating3 秒后变为 active。active 状态用户才能登录creating 状态登录会返回“用户尚未激活”。密码错误返回 401连续错误 5 次后账号锁定 300 秒。账号锁定期间即使输入正确密码也返回 403。登录成功返回 200并带一个 token 字段。选择这个项目是因为它涵盖了功能测试和接口自动化中很典型的点前置用户状态、异步等待、异常场景断言、锁定状态验证。你在真实项目中遇到的很多接口本质上都是这种“先准备数据、再验证业务流程”的模式。4.2 环境准备与项目结构本地环境需要 Python 3.8 及以上版本并安装下面的依赖库pip install flask requests pytest项目结构建议如下mock_app/ ├── login_service.py ├── requirements.txt └── tests/ └── test_login.py在真实项目中login_service.py 这种文件不会只是给测试用的它就是一个“被测服务”。这里为了方便你独立运行把它写成一个完整的 Flask 服务。requirements.txt 内容如下flask requests pytest如果你所在项目的版本管理要求严格建议把这些依赖锁定到具体版本本文示例以常见环境为例重点演示测试思路。4.3 编写被测服务 login_service.py下面创建一个文件mock_app/login_service.py写入完整代码# 文件路径mock_app/login_service.py import time import uuid import threading from flask import Flask, request, jsonify app Flask(__name__) LOCK_THRESHOLD 5 LOCK_SECONDS 300 # 模拟用户表 users_db { admin: { password: 123456, status: active, fail_count: 0, locked_until: 0 } } def _create_user(username, password): 初始化模拟用户状态先置为 creating。 users_db[username] { password: password, status: creating, fail_count: 0, locked_until: 0 } def _active_user(username): 模拟异步任务3 秒后用户状态变为 active。 time.sleep(3) if username in users_db: users_db[username][status] active app.post(/api/users/create) def create_user(): 创建用户接口通过线程模拟异步创建。 data request.get_json(silentTrue) or {} username data.get(username, ).strip() password data.get(password, ) if not username or not password: return jsonify({code: 400, message: 用户名和密码不能为空}), 400 if username in users_db: return jsonify({code: 400, message: 用户已存在}), 400 _create_user(username, password) threading.Thread(target_active_user, args(username,), daemonTrue).start() return jsonify({code: 0, message: 创建任务已提交}), 200 app.get(/api/users) def get_user(): 查询用户状态用于测试脚本轮询。 username request.args.get(username, ).strip() user users_db.get(username) if not user: return jsonify({code: 404, message: 用户不存在}), 404 return jsonify({ code: 0, data: {username: username, status: user[status]} }), 200 app.post(/api/login) def login(): 登录接口包含连续错误锁定逻辑。 data request.get_json(silentTrue) or {} username data.get(username, ).strip() password data.get(password, ) user users_db.get(username) if not user: return jsonify({code: 1001, message: 用户不存在}), 401 if user.get(status) ! active: return jsonify({code: 1004, message: 用户尚未激活请稍后重试}), 400 now time.time() if user[locked_until] now: return jsonify({code: 1003, message: 账号已锁定请稍后再试}), 403 if user[password] ! password: user[fail_count] 1 if user[fail_count] LOCK_THRESHOLD: user[locked_until] now LOCK_SECONDS return jsonify({code: 1002, message: 错误次数过多账号已锁定}), 403 return jsonify({ code: 1002, message: f用户名或密码错误剩余尝试次数 {LOCK_THRESHOLD - user[fail_count]} }), 401 user[fail_count] 0 user[locked_until] 0 token uuid.uuid4().hex return jsonify({ code: 0, message: 登录成功, data: {token: token} }), 200 if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)这段代码中的关键点有六个。第一users_db是内存字典等价于真实项目中的数据库。服务重启后数据会清空非常适合本地学习不会污染环境。第二创建用户接口用threading.Thread模拟异步任务。真实项目中创建用户可能是消费消息队列或后台任务调度测试时不需要等它同步完成而是轮询查询状态。第三状态字段status控制用户是否可登录。这样做的原因是为了模拟“不是所有测试数据创建完成后立即可用”的真实情况。第四密码错误使用fail_count累加达到LOCK_THRESHOLD后写入locked_until。这是一个非常典型的业务规则值得用自动化用例去覆盖。第五锁定判断在密码校验之前。所以“账号已锁定”优先于“密码是否正确”返回保证第 6 次输入正确密码时也会被拦截。第六uuid.uuid4().hex用于生成随机 token。真实系统中一般会用 JWT 等方式但这里简化成随机字符串不影响测试思路。4.4 编写自动化测试用例 test_login.py先启动被测服务。打开一个终端进入mock_app目录执行python login_service.py看到类似下面的输出说明服务已经启动成功* Running on http://127.0.0.1:5000 * Debug mode: on再打开另一个终端创建mock_app/tests/test_login.py文件。这个测试文件会用到 while 循环来完成两件事创建用户后轮询等待用户状态变成 active锁定用例中循环发送错误密码。先看完整代码# 文件路径mock_app/tests/test_login.py import time import uuid import pytest import requests BASE_URL http://127.0.0.1:5000 # 前 4 次错误密码返回 401第 5 次错误会触发锁定并返回 403 FAILED_TIMES_BEFORE_LOCK 4 def create_user(username, password, timeout15, interval0.5): 创建用户并轮询等待用户状态变为 active。 resp requests.post( f{BASE_URL}/api/users/create, json{username: username, password: password}, timeout5 ) if resp.status_code ! 200: raise AssertionError(f创建用户失败: {resp.text}) start_time time.time() while True: # 如果超过最大等待时间主动抛出超时异常 if time.time() - start_time timeout: raise TimeoutError(f用户 {username} 在 {timeout}s 内未创建完成) resp requests.get( f{BASE_URL}/api/users, params{username: username}, timeout5 ) if resp.status_code 200 and resp.json().get(data, {}).get(status) active: print(f用户 {username} 已激活开始后续测试) return time.sleep(interval) def random_username(prefix): 生成不重复的用户名避免多次运行互相影响。 return f{prefix}_{uuid.uuid4().hex[:8]} def test_login_success(): 正常场景输入正确用户名密码登录成功。 username random_username(test_success) password Test123 create_user(username, password) resp requests.post( f{BASE_URL}/api/login, json{username: username, password: password}, timeout5 ) assert resp.status_code 200 body resp.json() assert body[code] 0 assert len(body[data][token]) 32 def test_login_with_wrong_password(): 异常场景密码错误时返回提示信息。 username random_username(test_wrong) password Correct123 create_user(username, password) resp requests.post( f{BASE_URL}/api/login, json{username: username, password: Wrong123}, timeout5 ) assert resp.status_code 401 body resp.json() assert body[code] 1002 def test_lock_after_five_failed_attempts(): 业务规则连续错误 5 次后账号锁定。 username random_username(test_lock) password Lock123 create_user(username, password) wrong_count 0 # 前 4 次错误密码只返回 401不触发锁定 while wrong_count FAILED_TIMES_BEFORE_LOCK: resp requests.post( f{BASE_URL}/api/login, json{username: username, password: Wrong123}, timeout5 ) assert resp.status_code 401 wrong_count 1 # 第 5 次错误密码触发锁定 resp requests.post( f{BASE_URL}/api/login, json{username: username, password: Wrong123}, timeout5 ) assert resp.status_code 403 assert resp.json()[code] 1002 # 第 6 次即使输入正确密码也会被锁定状态拦截 resp requests.post( f{BASE_URL}/api/login, json{username: username, password: password}, timeout5 ) assert resp.status_code 403 assert resp.json()[code] 1003上述代码中create_user函数的 while 循环是核心。它先记录start_time然后无限循环查询用户状态。如果状态为 active 就直接返回继续后续测试如果超过timeout就抛超时异常。这里体现了上篇文章讲的 while 循环核心原则while True必须配套超时退出条件否则脚本有可能无限等下去。test_lock_after_five_failed_attempts中用 while 循环构造了 4 次错误登录。这个用例的价值在于验证“连续错误 5 次锁定”的业务规则而不只是验证“密码错误会提示”因此能更好保障后端逻辑不被错误修改。4.5 运行测试用例在终端中输入以下命令运行 pytestcd mock_app pytest -q tests/test_login.py运行成功后预期看到类似下面的结果3 passed in 12.35s三条用例全部通过说明被测服务的登录成功、密码错误、连续错误锁定这三个核心业务规则都符合预期。如果运行时报端口占用、依赖缺失或连接失败不要急着改代码可以参考下一节的常见问题排查。4.6 补充手工测试执行记录自动化接口用例通过后并不代表功能测试不需要做了。在真实项目中登录功能通常还需要手工验证页面交互、提示语展示、浏览器兼容性等内容。为了体现完整流程这里补充一张测试执行记录表模板用例编号用例标题执行结果实际结果缺陷编号TC-LOGIN-001正确用户名密码登录成功通过无无TC-LOGIN-002密码错误提示通过无无TC-LOGIN-003连续错误 5 次锁定通过无无TC-LOGIN-004用户名为空未执行等待修复用户名校验 Bug 后回归BUG-1024手工执行记录是测试报告的重要组成部分可以直观反映出当前版本的用例执行率和质量情况。自动化脚本和手工测试不是替代关系而是各司其职自动化负责高频重复回归手工负责探索性测试和用户体验验证。5. 常见问题与排查思路实战过程中最容易遇到的问题集中在环境搭建、while 循环逻辑和服务状态清理等方面。下面整理了一个排查表格问题现象常见原因解决思路启动服务报端口被占用5000 端口已被其他进程使用更换端口号比如port5001同步修改 test 文件中的BASE_URLpytest 报 requests 未找到当前 Python 环境没有安装 requests在运行 pytest 的同一终端执行pip install requests登录用例报“用户尚未激活”创建用户后立刻登录没有等待异步任务完成检查create_user中是否调用了轮询等待函数或等待时间是否太短测试脚本卡住不结束while 循环缺少超时退出条件给所有while True都加上超时判断和break/raise创建用户接口报“用户已存在”使用固定用户名重复运行用例服务进程没有重启改用随机用户名或重启login_service.py进程第 5 次错误密码没有返回 403前几次错误记录没有累加到同一个用户检查登录请求传参是否正确、用户名是否写错pytest 提示找不到测试文件当前工作目录不是 mock_app先cd mock_app再执行 pytest或使用pytest tests/test_login.py最值得注意的还是 while 循环导致的卡死问题。在接口自动化里一旦出现“脚本一直等待”的现象优先检查有没有设置超时时间。建议写一个统一的时间工具函数例如wait_until(predicate, timeout, interval)把轮询逻辑封装起来不要在每个用例里重复写。6. 软件测试最佳实践与工程建议6.1 测试数据要隔离和可恢复不管手工测试还是接口自动化都不要直接在生产环境随意造数据、删数据。测试应该使用独立的测试环境或本地环境并保证测试数据的可恢复性。上面的项目使用内存字典存储用户信息服务重启即清空就是故意让数据保持隔离。实际工作中如果测试依赖某些基础数据推荐在测试前执行数据准备脚本、测试后执行清理脚本。pytest 中可以用 fixture 实现 setup 和 teardown尽量保证每条用例独立不依赖其他用例的执行顺序。6.2 断言不能只判断状态码接口自动化最容易犯的错是只断言 HTTP 状态码。比如登录密码错误返回 401但如果后端因为异常也返回 500脚本可能误报。更合理的断言包括状态码、业务 code、message 字段、核心数据字段是否存在。在实际项目中接口返回结构往往很复杂建议把断言封装成公共函数。这样一旦业务码含义变化只需要修改公共断言模块。6.3 异步等待不要用固定 sleep很多人写异步接口测试时习惯用time.sleep(5)等待任务完成后再断言。这种做法不仅慢而且不稳定。如果某一次服务端处理超过 5 秒用例就会失败。更推荐的是用 while 循环加超时机制做轮询上面create_user函数已经演示了这种写法。脚本会以较短间隔反复查询任务状态任务一旦完成立即继续没有完成时也不会无限等待。这才是异步测试的正确姿势。6.4 重试机制要有边界网络抖动导致的偶发失败可以通过重试解决但重试必须设次数和间隔。重试逻辑里还要记录日志方便失败时排查到底哪一次请求出问题。如果重试后仍然失败不要吞掉异常继续执行应该把最终异常重新抛出让 pytest 记录失败否则用例会出现明明是失败却显示通过的情况。6.5 安全边界与合法授权无论是手工测试、接口测试还是性能测试都必须遵守一个前提只能测试你有

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价