ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

系统测试知识地图:内容矩阵、黑盒设计与性能压测实践

系统测试知识地图:内容矩阵、黑盒设计与性能压测实践 简介这是一份面向软件测试学习者与从业者的PPT课件聚焦系统测试这一关键环节系统讲解其概念、测试设计思路与完整测试流程。内容覆盖功能测试、性能测试、负载测试、压力测试、疲劳测试、易用性测试、安装测试、配置测试、文档测试、安全测试、恢复测试、回归测试、健全测试与交付测试等常见测试类型并结合客户与用户的沟通视角帮助读者理解系统测试在项目中的实际定位与执行要点。整套资料为一个PPT文件大小约668KB便于直接用于教学或自学。其中对每种测试方法均给出目标、方法与常用工具说明并包含测试设计中的软件攻击思路适合软件测试课程备课、测试工程师技能提升以及考前复习使用。目前已有203人学习下载。1. 系统测试为什么总在发布前夜暴露问题很多项目里单元测试全绿、集成测试通过可一进系统测试阶段缺陷单就像雪片一样飞过来。原因不复杂单元测试验证的是函数和模块边界集成测试验证的是接口拼装而系统测试是把整个系统当作一个黑盒在真实或接近真实的环境下按功能和性能需求去检查它是否达标。它从用户和客户视角对系统做攻击式体检能发现并发冲突、资源泄漏、权限绕过、安装卸载破坏环境这类“模块都对拼起来就错”的系统级问题。系统测试的内容非常宽从功能验证到性能压测从安全攻击到安装卸载光分类就有十多种。把这套知识框架拆成内容、设计、流程三块来消化是测试工程师走向独立负责系统测试计划的一条捷径。2. 系统测试内容矩阵十五类测试的分组与选型2.1 按验证目标重排测试矩阵按测试阶段划分经典的软件测试策略是单元测试、集成测试、系统测试、验收测试四步走系统测试夹在集成和验收之间是唯一把整个系统当作整体、以需求和性能指标为基准的阶段。常见的系统测试方案会把内容拆成功能、性能、负载、压力、疲劳、易用性、安装、配置、文档、安全、恢复、回归、健全、交付、演练这些项。如果一项一项排进计划很容易在资源冲突时抓不住重点。我一般会按验证目标把它们分成四组功能与体验、性能与可靠性、安全与容错、变更与交付控制。2.1.1 功能与体验组功能测试是覆盖产品全部功能点确认实现且实现正确回归测试阶段可以借助自动化工具反复执行。易用性测试跟功能测试结合通过观察用户操作、录像回放来评估学习成本和操作一致性。安装测试要覆盖典型部署场景不光是“装上能跑”还要看能否安装所有需要的文件和数据、是否破坏系统其他配置、能否中止安装并恢复现场、能否干净卸载。配置测试早期为了建实验室花掉两百万美元的例子虽然极端但今天操作系统版本、浏览器引擎、数据库版本的组合矩阵依然要按风险优先级砍。我的做法是按用户实际占比选 Top 3 配置做完整验证其余组合做健全级冒烟。文档测试需要单独一组人实施检查文档是否齐全、有没有多余的死文档、内容是否规范一致配置信息是否正确。很多项目把文档测试省略掉结果运维手册和实际行为对不上交付后徒增大量支持工单。文档里的配置项和 CI 配置保持一致这一点常常被忽略但阅读文档的用户第一步就是照它的配置去操作。2.1.2 性能与可靠性组这组测试目标容易混淆性能测试检验指标是否达标且能保持负载测试在人为设置的大数据量、大访问量场景下找问题和并发访问场景结合最紧压力测试反向操作人为减少内存、硬盘、网络、CPU、数据库响应时间等资源观察系统是崩掉还是进入可控的降级疲劳测试按经验连续 72 小时频繁使用功能用来暴露内存缓存的缓慢增长和连接池耗尽这类长时间运行才出现的问题。这四类都依赖工具而且被测环境尽量和生产配置一致否则结果只能作为相对参考不能作为容量规划依据。2.1.3 安全与容错组安全测试检查三件事系统是否带毒、是否加密正确、在非授权的内部或外部用户访问或故意破坏时是否出现错误。恢复测试直接模拟系统崩溃、硬件损坏、病毒入侵然后验证被破坏的环境和数据能否恢复。演练测试在交付前用相似用户环境走真实业务大型赛事 MIS 系统在正式赛事之前用小型赛事做演练就是这类测试的典型前置实践。安全与容错测试需要独立环境因为恢复测试会产生脏数据和残缺状态不能放在和开发共享的服务器上跑。2.1.4 变更与交付控制组健全测试是系统测试的准入闸门正常安装后跑一遍主要功能的常规用例确认功能性能基本可用、文档齐全再来安排其他测试内容。回归测试检查系统变更后有没有引入新错误或者旧错误重现执行时机是每次 Build 之后和稳定期测试阶段依赖测试用例库和缺陷报告库挑选回归范围。交付测试也叫稳定期测试目标是关闭所有缺陷报告确保系统达到预期交付标准该阶段出现新 Bug 要谨慎处理紧急修复必须走快速回归通道不能因为赶交付就绕过验证。分组包含测试验证目标是否需要工具功能与体验功能、易用性、安装、配置、文档功能正确、安装卸载可靠、文档一致回归阶段可用自动化工具性能与可靠性性能、负载、压力、疲劳指标达标并稳定长时间运行不退化大部分场景需要压测工具安全与容错安全、恢复、演练权限与加密正确灾难可恢复恢复测试依赖专门环境变更与交付控制健全、回归、交付变更不引入新缺陷交付标准达成回归依赖用例库和缺陷库排期时这张表可以当作工作量估算的参考功能与体验组占系统测试人力的一半以上性能与可靠性组通常由专职性能测试工程师负责安全与容错组需要提前申请独立环境变更与交付控制组嵌在持续集成流水线里。实际执行时健全测试跑在最前面性能测试要在功能稳定后才能给出有效数据恢复测试则建议放在绝大多数功能缺陷关闭之后否则脏数据会干扰后续回归。2.2 客户与用户校准测试目标的利益相关者视角客户和用户是两类利益相关者最终决策权在客户。用户用得好不好体现在易用性测试和操作流程用例客户愿不愿意验收取决于合同约束的需求和交付标准。系统测试计划要从这两条线同时对齐需求一条线对着用户使用场景拆用例一条线对着客户验收标准梳理交付清单。客户提的需求有时候是错的这很正常。测试过程中发现需求矛盾要在评审时当面提不要等到测试报告里写一句“需求不清晰”就完事。沟通时带着可选方案让客户理解测试资源约束和排期影响比直接拒绝或者默默背锅都有用。这一点在交付测试阶段尤其关键需求理解偏差积累到最后往往变成双方各执一词。3. 黑盒测试设计软件攻击与异常注入3.1 为什么系统测试几乎只做黑盒系统测试面对的是完整可运行的系统测试人员手里通常只有需求文档和操作手册没有内部实现细节。黑盒技术因此成为主选它不依赖代码结构换一种实现方案用例依然有效同时黑盒能够站在外部视角确认需求是否真正满足避免测试人员陷入“哪个分支没走到”的内部细节。但这不等于黑盒测试就是随便点系统级黑盒用例的来源必须是需求追踪矩阵加用户场景拆解每一条需求至少映射一个正向用例和一个反向用例。这个覆盖思路在功能测试和易用性测试的设计里尤其重要。3.2 软件攻击像挑剔用户一样找破绽软件攻击是系统测试设计里很实用的一类思路本质是故意用不符合常理的操作试探系统。常见做法包括输入超长字符串、在最不该为空的地方留空、重复点击按钮、输入 SQL 片段或保留字、安装过程中杀掉进程再重装、修改系统时间后继续跑业务流程。这些攻击不一定期望系统报错而是观察系统在异常操作下是否给出明确且可理解的反馈。如果直接崩溃、白屏、数据错乱那才是真正要修复的缺陷。攻击面攻击动作示例预期表现输入超长字符串、空值、特殊字符有约束提示不崩溃、不丢数据流程重复提交、快速双击幂等或给出处理中提示状态安装中杀进程、卸载到一半断电可恢复现场或明确提示环境修改系统时间、切换数据库业务逻辑不产生脏数据执行软件攻击要带着记录表每个动作配上预期和实际结果比笼统地在缺陷单里写“系统不稳定”更有说服力。攻击用例和正常用例混在同一轮执行里还有一个好处能在功能测试之外顺便覆盖掉一部分安全测试和恢复测试的场景节省独立测试的时间成本。3.3 用例骨架用 pytest 做系统级黑盒验证系统测试不排斥代码。对 Web 系统接口层的系统测试可以用 pytest 组织参数化用例统一管理等价类、边界值和攻击载荷。下面是一个登录场景的系统级用例集只面向被测系统的对外接口不关心服务端内部实现。import pytest import requests BASE http://staging.example.com/api # 等价类、边界值和攻击载荷集中在参数化用例里 pytest.mark.parametrize(username,password,expected, [ (admin, pass-123456, 200), # 等价类合法凭据 (admin, wrong-password, 401), # 等价类非法凭据 (, pass-123456, 400), # 边界空用户名应被拒绝 (admin, p * 17, 400), # 边界超过16位的密码 (admin--, anything, 400), # 软件攻击SQL注入特征串 (admin, or 11, 401), # 软件攻击绕过尝试 ]) def test_login_system_level(username, password, expected): resp requests.post(f{BASE}/login, json{username: username, password: password}, timeout3) assert resp.status_code expected从参数到断言的逻辑是这样走的parametrize 每跑一条用例都会发起独立请求相互之间不共享登录态因此每条用例的预期状态码是确定的合法和非法凭据落在等价类空用户名和 17 位密码落在边界值两个带单引号的输入属于软件攻击载荷。timeout3 给每个请求设了上限防止服务端响应卡死时整个用例集一起挂掉。这个骨架扩展到下单、支付、文件上传等场景时只需替换接口路径和请求体同一套参数化框架可以继续用。实际执行需要注意三点。第一BASE 要指向独立的系统测试环境不要在本地 localhost 上做这类验证本机没有真实的网络策略、委托配置和风控逻辑。第二密码策略如果和“16 位上限”不一致边界值要跟着需求规格改参数化用例的注释也需要同步更新否则别人维护时看不懂为什么选 17 位。第三接口返回的响应体建议额外记录到日志断言失败时可以从日志里看出是参数问题还是系统问题避免重复复现。3.4 测试数据准备与清理系统测试的用例之间要尽量独立。最省事的做法是给每条用例准备独立账号或独立订单测试脚本在开始前预置数据结束后按标识清理。常见做法是用事务包裹初始化脚本或者插入一条带特定前缀的记录清理时按前缀删除。不要在测试库直接复用生产数据副本里面往往混着脱敏不完整的脏数据会导致断言结果不可解释。4. 性能、负载、压力与疲劳测试压测参数怎么设4.1 四类测试的分工边界性能测试定基调负载测试找上限压力测试看底线疲劳测试盯长稳。四类测试的操作方式和结果输出完全不同排期也不能混在一起。测试类型操作方式验证目标典型结果输出性能测试按预期负载运行指标是否达标且能保持P95 响应时间、TPS、错误率负载测试逐步加压找拐点特定高负载下是否出错最大支撑并发数、拐点位置压力测试缩减资源看表现资源紧缺时是否可预见地失败内存不足或 CPU 抢占时的行为疲劳测试连续 72 小时高频操作长时间运行是否性能退化内存趋势、句柄数、连接池水位执行顺序上第一轮先跑性能测试拿到基线数据第二轮做负载测试把并发从低到高逐渐加记录 TPS 和响应时间的拐点压力测试不需要全系统范围做选负载测试找到的瓶颈点在其附近缩减内存或网络资源观察系统是进入降级还是直接宕掉疲劳测试要等功能缺陷收敛后再启动否则 72 小时里每次修复都要重启被测环境前面的数据全部作废。4.2 工具选型和 JMeter 参数说明工具层面JMeter 免费、社区活跃适合接口级脚本和多线程压测Gatling 用 Scala DSL 写场景更清晰生成的 HTML 报表适合直接贴测试报告商业工具的优势在于企业级协议覆盖和服务端资源监控预算充足时可以考虑。接口简单、团队熟悉 Python 的项目直接写并发脚本也完全够用。JMeter 里最关键的配置在 Thread Groupnum_threads 决定虚拟用户总数ramp_time 决定用户启动爬坡时间duration 配合 schedulertrue 限制总运行时长。下面是一组典型负载测试配置ThreadGroup stringProp nameThreadGroup.num_threads200/stringProp stringProp nameThreadGroup.ramp_time60/stringProp stringProp nameThreadGroup.duration3600/stringProp stringProp nameThreadGroup.schedulertrue/stringProp /ThreadGroup200 个用户在 60 秒内逐步上线总持续 3600 秒这是给系统一个“从冷启动到满载”的真实过渡过程。如果是压力测试可以把 ramp_time 缩到 5 秒让并发瞬间打满直接看系统在突袭式流量下的表现疲劳测试则不需要重启线程组把 duration 改成 259200 秒配合聚合报告定期落盘让 JMeter 跑满 72 小时。4.3 用 Python 写并发脚本和 72 小时监控自研压测脚本最难处理的是并发控制和数据统计。下面这个脚本用 ThreadPoolExecutor 模拟固定数量的虚拟用户每个用户独立跑固定轮次收集延迟后计算分位数import concurrent.futures import statistics import requests import time TARGET https://staging.example.com/api/home CONCURRENCY 50 # 虚拟用户数按生产峰值折算 EACH_USER_LOOPS 20 # 每个用户循环请求次数 def run_user(no): latencies [] for _ in range(EACH_USER_LOOPS): start time.perf_counter() requests.get(TARGET, timeout5) latencies.append(time.perf_counter() - start) return latencies all_latencies [] with concurrent.futures.ThreadPoolExecutor(max_workersCONCURRENCY) as pool: for user_lat in pool.map(run_user, range(CONCURRENCY)): all_latencies.extend(user_lat) all_latencies.sort() n len(all_latencies) print(f请求总数: {n}) print(f平均耗时: {statistics.mean(all_latencies):.3f}s) print(fP50: {all_latencies[n // 2]:.3f}s) print(fP95: {all_latencies[int(n * 0.95)]:.3f}s) print(fP99: {all_latencies[int(n * 0.99)]:.3f}s)ThreadPoolExecutor 的 max_workers 决定并发度pool.map 保证所有虚拟用户结束后统一汇总结果每个用户连续请求 20 次既能观察单用户请求序列的延迟抖动也足够算出稳定的 P95。平均响应时间只能说明整体体验P99 才是判断系统是否有长尾请求的关键指标。想把固定轮次改成固定时长在 run_user 的循环里包一个截止时间判断即可。疲劳测试阶段压测脚本本身不是重点服务端资源趋势才是。下面这个采集脚本用 psutil 每隔 60 秒打印一次 CPU、内存和连接数配合 nohup 重定向到日志文件跑满 72 小时import psutil import time INTERVAL 60 # 每60秒记录一次疲劳测试持续72小时 while True: mem psutil.virtual_memory() print(fCPU {psutil.cpu_percent(interval1):.1f}% fMEM {mem.percent:.1f}% fconn {len(psutil.net_connections()):d}, flushTrue) time.sleep(INTERVAL)提示72 小时疲劳测试启动前确认日志分区至少有 2GB 余量采集脚本用 nohup 或 systemd 托管输出重定向到独立日志文件否则 SSH 断开会让采集中断72 小时的数据链就断了一截。这里的 conn 统计的是当前进程持有的网络连接数如果它随时间单调递增而不回落基本可以断定连接池或句柄存在泄漏。日志建议按小时 rotate分析时把每分钟的 CPU、MEM、conn 画成折线就可以直接看出性能退化是从第几个小时后开始的这比看 72 小时结束后的单一报告更能定位问题。5. 健全测试、回归范围与交付测试的收尾细节5.1 健全测试系统测试的准入闸门健全测试安排在每次 Build 之后、大面积测试开始之前。正常安装系统跑一遍主功能用例顺手检查文档是否齐全几分钟内能跑完就算通过。这一步不要设计太重的用例目的只有一个过滤掉连主流程都走不通的版本。否则后面的性能测试、恢复测试都是白跑被测对象的初始状态就不合格。5.2 回归范围变更影响面映射用例库回归测试依赖测试用例库和缺陷报告库两类数据。常见做法是把最近一周的代码变更文件列表映射到用例库里的模块标签git diff $(git log --until1 week ago --format%H -1) --name-only上面命令先拿到一周前的提交哈希再对这周的代码提交列出所有改动文件。得到输出后按模块归并然后到缺陷报告库拉出最近两周缺陷分布两个集合取并集就是本轮回归的可信范围。历史缺陷集中的模块即使没有变更也建议每次回归都覆盖因为那些模块大多处于“靠人肉补丁维持稳定”的状态最容易旧错重现。5.3 交付测试新缺陷要走沉淀流程交付测试也叫稳定期测试目标是关闭所有缺陷报告确保达到交付标准。这个阶段出现新 Bug 不要马上修先评估影响面确认影响核心路径再动手。修复完成之后走两条路一条是执行该模块对应的回归用例另一条是补一条针对本次根因的回归路径。按这个原则交付期处理的每个缺陷都会沉淀进回归用例库下一次回归的范围也随之扩大。最怕的是交付期新缺陷只修不记缺陷关闭了两周后相同问题换一种输入方式再次出现。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进