c# 怎么自动化测试

接入与开发

讲清 C# 项目里单元测试与 UI 自动化测试怎么起步:选框架、拆测试边界、跑通 CI 与稳定度排查。

写 C# 的团队常卡在同一处:业务代码能跑,但没人敢改,一改就怕线上崩,想补测试又不知道从哪下手,UI 测试更是写完三天就红一片。这篇按落地顺序给一套做法——先定测什么、再选框架和目录结构,然后解决外部依赖和 UI 稳定性这两个最容易翻车的地方,最后接进流水线。

写 C# 的团队常卡在同一个地方:业务代码能跑,但没人敢改,一改就怕线上崩。想补测试又不知道从哪下手,UI 自动化更是写完三天就红一片,最后没人看。这篇按落地顺序给一套做法——先定测什么,再选框架和目录结构,然后解决外部依赖和 UI 稳定性这两个最容易翻车的地方,最后接进流水线。

前提说清楚:下面的方案适合已经有稳定迭代节奏的 C# 项目。如果还在原型验证阶段,或者写完下周就下线的临时脚本,硬补测试反而拖慢节奏,先跳过这一轮。

先想清楚测什么:别一上来就追求覆盖率

覆盖率是个结果指标,不是起点。一上来盯着数字,结果就是测了一堆 getter/setter 和纯转发层,真正容易出事的地方反而没人碰。按风险排序更实用:分支多、判断逻辑绕、出错了影响面大的地方先测。

模块类型优先测吗原因
纯逻辑计算、金额与权限判断、状态机优先分支多,改一处容易牵动别处
数据转换、格式校验优先边界条件密集,手工验证成本高
接口转发、透明代理类可以后放本身没什么判断,测了收益低
依赖外部接口/数据库/时钟/随机数先隔离再测不能隔离就没法稳定重复跑

落地动作很具体:拿一张表列出模块、风险等级、有没有外部依赖,然后排一个两周的补测顺序。只产出一张表,不写代码,先把顺序定下来。

选框架和目录结构:一次定好,后面少折腾

C# 里主流是 xUnit、NUnit、MSTest 三家,差别主要在断言写法、并行执行的默认行为、参数化测试的表达方式。没有哪家绝对更好,团队熟悉哪个就用哪个,换框架的收益通常抵不上迁移成本。

xUnit
默认并行跑测试类,断言风格偏简洁,新项目用得较多
NUnit
特性体系完整,参数化和分类标记的表达比较直观
MSTest
和 Visual Studio 及微软工具链贴合度高,老项目常见
  • 测试项目单独建工程,命名和被测工程对应,确保测试代码不会被打进发布产物
  • 测试类按「一个类对应一个被测类」切分,别把所有测试塞进一个大文件
  • 方法名写清前提条件和期望结果,看名字就知道这条挂了意味着什么
  • 同一逻辑多组入参优先用参数化特性,别复制十份几乎一样的测试方法
  • 目录按被测代码的命名空间镜像,找起来不用猜

项目规模大、多个测试工程并存时,还要提前定好测试代码的引用方式:被测逻辑尽量抽到可被独立引用的类库里,避免测试工程反向依赖可执行程序。这一步没做好,后期会出现测试跑不起来、循环依赖的麻烦。

把外部依赖隔开:单元测试跑得快才有人愿意跑

单元测试的价值一半在速度。要跑两分钟以上,本地就没人在提交前执行,等于白写。而拖慢速度的通常就是真实数据库、真实 HTTP 调用、真实文件读写、系统时钟和随机数。

  1. 找出所有外部接触点把数据库、HTTP、文件、时钟、随机数全部抽象成接口,在业务代码里只依赖接口,不直接 new 具体实现。
  2. 准备替身简单场景手写一个假实现就够,行为复杂的用 mock 框架。替换目标是让测试里的时间、返回值完全可控。
  3. 分离集成测试依赖真实环境的用例单独放一个工程或单独打标签,和单元测试分开跑,前者慢且需要环境,后者应是毫秒级。
  4. 验证隔离效果整个单元测试套件跑完超过一两分钟,基本说明隔离没做干净,回头继续拆。

判断标准很简单:断网之后单元测试还能全绿,隔离就算做到位了。做不到说明还有地方在偷偷连外部。

UI 自动化:先解决不稳定,再谈覆盖面

UI 测试最容易死在两个地方:元素定位太脆,和等待方式不对。界面一改坐标和层级路径就全断,固定 sleep 又会让整套测试又慢又随机。这两个问题不解决,覆盖多少用例都是负担。

做法问题改成什么
按坐标或层级路径定位界面微调就失效用 AutomationId、可访问性名称等稳定标识
固定 sleep 等待快机器上浪费、慢机器上仍失败等条件成立,并给每个操作设超时上限
UI 覆盖所有分支维护成本吃掉收益UI 只测主流程和关键分支
超时后无证据失败无法复现超时自动截图并保留当时的页面状态

排查顺序建议固定下来:先确认是元素找不到、还是页面没加载完、还是环境不一致(分辨率、驱动版本、登录态)。三类原因的处理方式完全不同,混着查会浪费大量时间。

  • 先在本地固定环境跑一遍,确认不是环境差异导致的失败
  • 再看失败截图,定位是元素缺失还是渲染未完成
  • 检查选择器是否还能在页面上唯一命中
  • 确认登录态和数据准备是否正确
  • 连续两次无故失败就单独拿出来查,别靠重跑掩盖

UI 用例的数量控制在主流程和关键分支就够了,回归细节交给单元测试和接口测试。想了解自动化工具的分类和适用场景,可以到 /tools/speed-test 对照着选,但工具只是载体,稳定度靠的是上面这套写法。

接进流水线:让测试结果真的能拦住问题

测试写完了不进流水线,等于只有写的人知道。关键是把不同层级的测试放在不同触发点上,别让整套都堆在每次提交。

  • 提交时跑单元测试,反馈要在几分钟内出来
  • 合并前跑单元测试加接口测试
  • UI 测试按天或按发布节点跑,不必每次提交都跑
  • 不稳定用例先隔离进单独队列并记录,不要靠重跑掩盖
  • 失败要带可复现信息:随机种子、输入数据、失败截图或日志片段

判断一条失败报错有没有价值,看它能不能让另一个人在不问原作者的情况下复现。缺随机种子、输入数据、截图的报错,等于没报。

如果项目打算接模型做代码审查或自动生成单测,可以参考站内 AI 代码审查和单测生成那几篇的分工来做,但生成的用例必须人工过一遍边界条件。生成结果适合当草稿,不适合直接合并。

另外,如果测试里会调用外部 API,成本和稳定性都要提前评估。各家官方 API 的当前价格和限制到 /price-watch 核对当时的数字,不要凭记忆估算,价格调整后测试预算会跟着变。

下一步按大纲把补测顺序表列出来,再挑一个风险最高的模块先跑通单元测试,最后接进流水线。想先了解自动化工具选型,可以到工具目录对照适用场景。

常见问题

C# 单元测试框架选哪个,会互相影响吗?

xUnit、NUnit、MSTest 都能满足日常需求,差异主要在断言风格、参数化写法和默认并行策略。同一个解决方案里最好统一成一个,混用会让配置和运行命令变复杂。团队没历史包袱就选社区资料最多的那个,后面换成本也不低。

UI 自动化测试总是不稳定,是不是干脆别做?

不是别做,是要缩小范围。把 UI 用例压到登录、下单、支付这类主流程,其余的用单元测试和接口测试覆盖。剩下的不稳定大多来自固定等待和脆弱定位,换成条件等待加稳定标识后能明显改善。仍偶发失败的用例单独隔离,别让它污染整体结果。

小团队没时间写测试,最低限度该做什么?

先给最容易出错、改动最频繁的那几个函数补测试,尤其是金额计算、权限判断、状态流转这类逻辑。再给每个 bug 修复补一条回归用例,防止同一个问题复发。这两件事投入不大,但能挡住大部分线上事故,比追求覆盖率数字实在。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。