尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C#自动化测试平台:用Excel配置测试序列的完整实践

C#自动化测试平台:用Excel配置测试序列的完整实践 先说一下这个项目本质上不是什么新概念但胜在够“省心”。我在好几个业务线里都见过类似的“手工回归测试占大头、自动化脚本写一堆却没有多少复用价值”的尴尬局面。后来有团队把测试用例从代码里彻底搬出去扔到 Excel 里按序列配置反而盘活了整个自动化进程。这篇就围绕这个 C# 自动化测试平台源码把 Excel 测试序列配置从设计到实战的完整思路拆给你包括表格字段怎么规划、引擎怎么解析、序列怎么执行、遇到翻车怎么排查。无论你是测试开发、后端转测试还是想给团队搭建一套轻量级自动化底座的开发都可以照着这套思路落地。1. 技术选型与架构设计为什么是 C#、为什么用 Excel 做序列配置1.1 技术选型背后的考量做自动化测试平台第一道沟就是技术栈选择。你可能会问现在 Python 的 pytest、Java 的 TestNG 不是生态很成熟吗为什么偏偏用 C#我实际接触下来C# 在这类场景里有几个很实在的优势。首先是团队技术背景。很多做 Windows 桌面端、上位机、企业内部系统的团队开发主力就是 C#/.NET 背景。测试开发用同一门语言和开发沟通成本低代码审查、维护也更顺畅。如果团队本身就是 C# 栈硬塞一个 Python 框架等于凭空多维护一套语言体系不划算。其次是 .NET 在 Windows 环境下的工具链和调试体验。Visual Studio 的断点调试、即时窗口、性能分析对定位测试框架本身的 bug 非常有帮助。测试框架一旦跑起来问题往往不在业务代码而在框架自身逻辑——比如 Excel 解析边界、断言表达式解析失败、序列中断位置不明确这些在 VS 里都能快速揪出来。再说 Excel 作为配置载体。很多测试人员并不是不会写代码而是写代码的成本远高于写表格。用 Excel 配置测试序列等于是把“测试用例设计”和“测试执行逻辑”彻底解耦。业务测试人员只需要关注“我要测什么”不用关心“代码怎么执行”。而且 Excel 天然支持批注、颜色标记、多人评审领导也好验收。把测试序列从代码里解放出来最大的收益是让参与编写和维护用例的人指数级扩大。1.2 平台整体架构与核心模块这套平台我建议按四层结构来设计每一层职责单一后续扩展不会牵一发动全身。第一层是配置层也就是 Excel 工作簿。每个 Sheet 可以理解为一组测试序列每一行是一个测试步骤每一列定义这个步骤的属性和参数。还有配套的环境配置文件通常是一个 JSON 或 XML用来区分 dev、test、prod 环境的主机地址、账号密码、超时时间等。第二层是解析层负责把 Excel 的行列结构转换成内存中的测试步骤模型。它会读取表头按列名映射字段做数据合法性校验把字符串参数转成对应的对象类型。这一步很关键因为在 Excel 里所有内容本质上都是字符串如果不做类型转换和规范性检查执行层拿到乱七八糟的数据会非常头疼。第三层是执行引擎这是整个平台的心脏。引擎拿到解析后的测试步骤列表按顺序逐条执行。每个步骤对应一个“关键字处理器”比如 HTTP 请求、数据库查询、变量赋值、断言比较、条件判断、循环控制等。引擎还要负责变量替换、上下文传递、失败中断策略和日志输出。第四层是报告层。执行过程中产生的日志、断言结果、请求响应摘要、截图如果做了 UI 自动化统一汇总生成 HTML 或 Markdown 报告并可以对接企业内部的消息通知比如钉钉、企业微信机器人做结果推送。这套分层看起来不复杂但每层之间通过明确的接口通信后续你想把 Excel 配置换成数据库配置或者把执行引擎从同步改为异步分布式都不会伤筋动骨。2. Excel 测试序列的核心设计表格即用例结构决定上限2.1 测试序列字段设计与用法解析Excel 测试序列配置不是随手拉几列就完事。我见过不少项目第一天配序列时很顺畅跑到第 50 个用例就开始乱套——字段含义不清晰、参数格式不统一、断言方式混乱。所以表头设计必须一开始就定清楚每一列都有唯一语义。下面是我常用的一套核心列结构你可以根据实际场景裁剪列名是否必填说明示例用例ID必填测试用例唯一标识TC-Login-001用例名称必填描述当前用例目标正常登录并获取 Token步骤序号必填同一用例内步骤执行顺序1、2、3关键字必填步骤动作类型HttpRequest / AssertEqual / SetVariable目标/接口条件必填请求地址或对象标识/api/login请求方式条件必填GET/POST/PUT/DELETEPOST请求头选填自定义请求头JSON格式{Content-Type:application/json}请求体/参数选填请求数据支持变量占位{username:${user}}期望结果条件必填断言的目标值code200断言方式条件必填Contains / Equal / JsonPathJsonPath超时时间选填步骤超时单位秒10是否启用选填Y/N跳过某步骤或用例Y提取变量选填从响应中提取数据到上下文token → $.data.token备注选填补充说明依赖前置用例登录这个结构的关键在于“提取变量”和“期望结果”分离。很多初版设计都是把提取逻辑写死在代码里导致新增一个用例就得改代码。把提取变量做成配置列由引擎去解析 JSONPath 或正则表达式用例的灵活性会大幅提升。再补充一个容易踩坑的细节Excel 列的顺序无所谓但表头名称必须和解析器里定义的映射模板完全一致。我建议在解析层加一个表头合法性校验如果发现无法识别的列名直接报错并列出支持的列名清单比事后排查快得多。2.2 关键字驱动的设计原则Excel 测试序列配置的灵魂就是关键字驱动。所谓关键字就是一个动作指令告诉引擎“这一步要做什么”。我在实际项目里把关键字分成四类清晰区分后维护成本降低很多。第一类是请求类关键字比如 HttpRequest、SqlQuery。它们负责和数据源交互返回结果写入当前步骤的上下文。以 HttpRequest 为例引擎会组合请求方式、地址、请求头、请求体发起 HTTP 调用把响应状态码、响应体、响应时间都存起来供后续步骤使用。第二类是断言类关键字比如 AssertEqual、AssertContains、AssertJsonPath。它们从上下文取实际值和期望结果做比较失败时抛出异常或标记步骤失败。断言类的设计要尽量丰富因为在自动化测试里80% 的用例失败都集中在断言环节。第三类是数据类关键字比如 SetVariable、ReadFromExcel。它们负责在上下文中设置变量、从外部数据源准备测试数据。特别是 SetVariable配合“提取变量”列能实现非常灵活的参数链路。第四类是控制类关键字比如 RunCase、Condition、Loop。它们解决测试序列的编排问题。比如你有一个依赖登录的用例集可以在序列最前面放一个 RunCase 类型的步骤调用“获取Token”这个公共用例再把结果注入上下文。关键字驱动的设计原则可以类比成积木。每个关键字是一块标准积木测试序列就是积木搭出来的模型。积木本身不复杂但组合方式千变万化。这样设计的好处是新增业务动作优先考虑是否能用现有关键字组合实在不行才扩展新关键字核心代码库始终保持精简。3. 从 Excel 到引擎核心代码与实现要点3.1 读取 Excel 的几种方案与选型C# 读取 Excel 文件常见的有四种方案。COM 组件Microsoft.Office.Interop.Excel在服务器上部署容易踩雷因为需要安装 Office 环境并发时稳定性也差我只会在本地调试时偶尔用。EPPlus 功能强大但商业使用有 License 限制个人学习无所谓公司项目建议慎重。MiniExcel 轻量、性能好适合只读场景但功能相对简单。我最终长期用的是 NPOI主要考虑三点开源免费无商用限制、不依赖 Office 环境、读写 .xls 和 .xlsx 都稳定。用 NuGet 安装 NPOI 很简单然后读取一个 Sheet 的核心代码大致是这样的using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; public ListDictionarystring, string ReadSheet(string filePath, string sheetName) { var result new ListDictionarystring, string(); using var fileStream new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); IWorkbook workbook new XSSFWorkbook(fileStream); var sheet workbook.GetSheet(sheetName); if (sheet null) throw new Exception($未找到Sheet: {sheetName}); var headerRow sheet.GetRow(0); var headerMap new Dictionaryint, string(); foreach (var cell in headerRow.Cells) { headerMap[cell.ColumnIndex] cell.StringCellValue.Trim(); } for (int rowIdx 1; rowIdx sheet.LastRowNum; rowIdx) { var row sheet.GetRow(rowIdx); if (row null) continue; var rowData new Dictionarystring, string(); foreach (var kv in headerMap) { var cell row.GetCell(kv.Key); rowData[kv.Value] cell null ? string.Empty : cell.ToString().Trim(); } result.Add(rowData); } return result; }注意这里用了FileShare.ReadWrite这个细节很重要。测试人员经常开着 Excel 文件查看用例如果引擎以独占方式打开文件就会报“文件被占用”。设置共享读可以在文件被占用时依然读到内容。3.2 序列解析器从行数据到测试步骤模型读到行的字典数据后下一步是转换成可执行的测试步骤模型。我通常会定义一个TestStep类public class TestStep { public string CaseId { get; set; } public string CaseName { get; set; } public int StepSeq { get; set; } public string Keyword { get; set; } public string Target { get; set; } public string Method { get; set; } public string Headers { get; set; } public string Body { get; set; } public string Expected { get; set; } public string AssertType { get; set; } public int Timeout { get; set; } public bool Enabled { get; set; } public string ExtractVariable { get; set; } }解析器做的事情就是读取字典并做类型转换和合法性校验。比如StepSeq必须能转成 intEnabled只有 Y/N 两个合法值Keyword必须存在于已注册的关键字字典里。一旦发现非法数据立即抛出明确的异常信息包括行号、列名和非法值。这里有一个我经历过很多次的设计教训解析阶段不要只报第一个错误就退出。更好的做法是把所有错误收集起来一次全部抛出对于动辄几百行步骤的序列来说这个体验差异非常大。3.3 执行引擎的主循环与上下文传递执行引擎的主循环核心是遍历步骤列表按关键字分发到对应处理器处理器执行完毕后返回结果并更新上下文。上下文用ConcurrentDictionarystring, object保存支持变量读写。引擎主循环的代码本质上是这样的public void Execute(ListTestStep steps, TestContext context) { foreach (var step in steps) { if (!step.Enabled) continue; context.CurrentStep step; try { if (!keywordHandlers.TryGetValue(step.Keyword, out var handler)) { throw new Exception($未注册的关键字: {step.Keyword}); } handler.Execute(step, context); } catch (Exception ex) { context.Logger.Error($用例 {step.CaseId} 步骤 {step.StepSeq} 执行失败: {ex.Message}); if (stopOnFailure) throw; } } }变量替换也是一个容易出问题的点。我在解析请求体、请求头、期望结果之前都会统一执行一次变量替换把${变量名}形式的占位符替换成当前上下文中的实际值。替换逻辑要注意循环引用和变量不存在时报错这两个问题否则排查起来很痛苦。断言的实现上我推荐把“实际值提取”和“比较逻辑”分开。比如断言方式是 JsonPath 时先从响应体按 JSONPath 提取出实际值再和期望结果做 Equal 或 Contains 比较。代码大致是public class AssertJsonPathHandler : IKeywordHandler { public void Execute(TestStep step, TestContext context) { var actual JsonPathHelper.Read(context.ResponseBody, step.Target); bool passed actual step.Expected; context.Logger.Info($断言JsonPath: {step.Target}, 期望: {step.Expected}, 实际: {actual}, 结果: {passed}); if (!passed) throw new AssertionException($断言失败: {step.Target}); } }3.4 环境隔离与报告生成环境隔离这块我用一个简单的 JSON 配置文件解决。不同环境维护不同的 baseUrl、数据库连接字符串、账号信息。运行时通过启动参数指定当前环境比如--envtest。所有请求地址在发送前会拼接上 baseUrl这样同一套 Excel 序列可以在不同环境间无缝切换。报告生成我用的是轻量自研方案。执行过程中把每个步骤的状态、耗时、请求摘要、响应摘要写入结构化日志执行结束后统一渲染成 HTML 报告。报告要按用例维度聚合一眼能看到通过率、失败步骤和失败原因。再配合企业微信机器人推送执行结果整个自动化的闭环就有了。4. 实操演示搭一套登录与信息查询的 Excel 测试序列4.1 业务场景与 Excel 配置示例我们拿一个最常见的业务场景来演示先登录获取 Token再携带 Token 查询用户信息。按这套平台的思路只需要建一个 Excel 文件两个 Sheet第一个 Sheet 放公共登录用例第二个 Sheet 放业务查询用例。第一个 Sheet 命名为公共用例内容大致如下用例ID用例名称步骤序号关键字目标/接口请求方式请求体期望结果断言方式提取变量是否启用TC-COMMON-LOGIN系统登录获取Token1HttpRequest/api/loginPOST{username:test01,password:123456}200Equaltoken → $.data.tokenY这里有两个关键点。第一HttpRequest处理器在执行成功后会把响应体存入上下文。第二“提取变量”按照 JSONPath$.data.token从响应体中提取到token变量。这样后续所有用例都能通过${token}引用。第二个 Sheet 命名为业务用例场景是查询当前用户信息| 用例ID | 用例名称 | 步骤序号 | 关键字 | 目标/接口 | 请求方式 | 请求头 | 期望结果 | 断言方式 | 是否启用 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | TC-USER-INFO-001 | 查询登录用户信息 | 1 | HttpRequest | /api/user/profile | GET | {Authorization:Bearer ${token}} | 200 | Equal | Y | | TC-USER-INFO-001 | 校验返回用户名 | 2 | AssertJsonPath | $.data.username | | | test01 | Equal | Y |第二步没有请求动作只有一个断言。AssertJsonPath处理器会把$.data.username从上下文中的响应体提取出来和期望结果test01比较。这样设计的好处是清晰第一步负责发起请求第二步专注校验一个步骤只做一件事定位问题非常容易。4.2 运行过程与结果解读执行时平台入口加载配置指定 Excel 路径和 Sheet 列表。引擎会先执行公共用例里的登录步骤提取到 token 后写入上下文再顺序执行业务用例。执行日志大致会是[INFO] 开始执行Sheet: 公共用例 [INFO] 步骤1: POST /api/login 请求发送 [INFO] 步骤1: 响应码 200, 耗时 230ms [INFO] 步骤1: 提取变量 tokeneyJhbGciOiJIUzI1NiJ9... [INFO] 开始执行Sheet: 业务用例 [INFO] 步骤1: GET /api/user/profile 请求发送, Header Authorization: Bearer eyJhbGci... [INFO] 步骤1: 响应码 200, 耗时 150ms [INFO] 步骤2: JsonPath $.data.username 实际值test01, 断言通过 [PASS] TC-USER-INFO-001 查询登录用户信息如果断言失败日志会标明具体步骤、实际值、期望值HTML 报告里也会用红色标出失败环节。整条链路的可追溯性非常强。4.3 遇到的前置依赖问题怎么处理实际配置过程中最容易遇到的一个问题是业务用例依赖登录但登录用例跑到一半失败了业务用例还在傻傻地继续执行。解决方案是在引擎里加一个“用例级失败中止”选项。比如配置文件里StopOnCaseFailtrue一旦某个用例断言失败后续依赖它的用例直接标记为 Blocked不再执行。同时支持在 Excel 的用例名称里通过约定前缀区分是否为核心前置用例非前置失败只记录警告不影响后续步骤。通过这种前置流程与变量依赖的配置方式测试序列可以做成模块化组合。后续即使登录逻辑变了只需要改公共用例一个地方所有依赖登录的用例都会自动生效。5. 常见问题排查与避坑技巧实录5.1 高频问题速查表现象可能原因排查方向与解决方案读取 Excel 失败文件被 WPS 或 Office 占用或文件损坏使用 FileShare.ReadWrite 打开文件避免独占锁检查文件是否被程序手动保存为 .xls 格式中文乱码请求参数编码问题检查 HttpRequest 处理器发送请求时是否指定 UTF-8对请求体做HttpUtility.UrlEncode或 JSON 序列化编码断言总是失败期望值和实际值类型不一致比如接口返回200是数字Excel 里写的是字符串需要在断言比较前统一类型建议统一走字符串比较变量替换后为空提取变量名拼写或 JSONPath 值不存在开启上下文快照日志在失败步骤前输出当前所有变量的键值用例间数据相互污染上下文变量全局共享未清理执行每个 Sheet 前重置非公共变量或在用例启动时执行清理步骤多个接口并发导致超时线程池设置过小或接口响应慢检查执行引擎的并发配置控制一次执行的最大并行数量也可以把超时时间调大并增加重试机制5.2 调试技巧与效率提升我在实际用这套平台调试 Excel 序列时养成了几个习惯极大减少了定位问题的时间。第一个习惯是开启“Dry Run”模式。所谓 Dry Run就是引擎只解析 Excel 但不真正发送 HTTP 请求而是把每一步的完整参数打印出来。这是一个价值极高的功能尤其适合确认变量替换、断言表达式、请求头拼接是否符合预期。你可以通过命令行参数或配置文件开关来控制实现起来并不复杂——在 HttpRequest 处理器里判断如果处于 Dry Run 模式只记录日志不发送请求。第二个习惯是对日志分级别。Info 级别记录正常执行流程Debug 级别记录上下文完整快照Error 级别记录异常堆栈。平时跑用例用 Info 级别排查问题时切到 Debug信息量完全够用不会因为日志太吵淹没关键信息。第三个习惯是给每个用例步骤生成“现场快照”。快照包括当前步骤的请求参数、响应摘要、断言结果、上下文关键变量的值。一旦用例失败报告里直接展示快照不用再翻大量日志。第四个习惯是 Excel 模板和用例版本管理。我坚持把测试 Excel 文件里的空模板和有效用例分开存放模板结构变化时通过版本号管理。同时用例文件纳入版本控制如果测试数据有变更可以快速对比历史版本。5.3 提升平台稳定性的细节建议这里再说几个对稳定性提升非常明显的小细节。第一Excel 文件里不要写公式。NPOI 读取单元格时如果单元格是公式拿到的是公式字符串而不是计算结果值。所以在测试配置里我要求所有单元格写入静态值绝不用 Excel 公式做动态计算。如果确实需要动态数据在请求体里使用${变量名}由引擎在运行时解析而不是由 Excel 计算。第二接口返回大 JSON 时上下文保存的响应体会很占用内存。建议只保留最近一次响应体如果需要历史步骤的响应显式提取到变量再保存。否则跑大量用例时内存会很快吃紧。第三超时设置要合理。我一般把接口类的步骤超时设置在 5 到 15 秒之间太短容易误报太长会拖慢整体执行。数据库操作类步骤超时适当放宽到 30 秒因为数据量大时 SQL 执行慢是正常的。第四请求频率控制。有些接口有并发限制或限流策略批量执行时容易触发服务端拒绝。可以在引擎里加一个全局的请求间隔配置比如每两个请求之间等待 100 毫秒牺牲一点速度换稳定在环境不稳定的企业内部系统上非常值得。6. 写在最后这套平台还能怎么升级这套基于 Excel 测试序列配置的 C# 自动化测试平台解决的核心问题是让测试用例从“代码”中解放出来让更多人有能力参与自动化测试的编写和维护。我在实际项目里用了大半年最大的感触不是技术有多复杂而是“用例维护成本”真的降下来了。以前开发和测试工程师为一个断言逻辑改来改去现在只需要在 Excel 里调整一行配置改完就能重跑效率提升非常直观。如果你打算基于这套思路继续扩展我建议优先考虑三个方向。第一是支持从 Excel 配置平滑迁移到数据库配置因为当用例数量上千条之后Excel 的检索和多人协作会成为瓶颈但配置结构可以保持不变。第二是接入 UI 自动化能力比如通过 Windows UI Automation 或 SikuliX 这类工具扩展新的关键字去覆盖桌面端和浏览器端测试。第三是加一个 Web 管理界面把序列配置、执行记录、报告查看都放到浏览器里完成这样测试开发团队和业务测试团队的分工会更清晰平台的通用性也会更强。最后分享一个小技巧如果你在公司内部推广这套方案不要一上来就追求功能齐全先把“HttpRequest AssertJsonPath SetVariable”这三个关键字跑通解决接口自动化里 80% 的核心问题。等大家用顺手了再逐步补充控制流、SQL 查询、并发执行这些高级特性。小而美的工具永远比大而全的平台更容易在团队里扎根。
返回列表