news 2026/9/18 5:31:18

.NET 测试生成实战指南:基于 code-testing-extensions 的 dotnet.md 扩展规范

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
.NET 测试生成实战指南:基于 code-testing-extensions 的 dotnet.md 扩展规范

.NET 测试生成实战指南:基于 code-testing-extensions 的 dotnet.md 扩展规范

【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills

导读

本文以 code-testing-extensions 技能体系中面向 .NET(C#/F#/VB)的语言扩展文件 extensions/dotnet.md 为核心,系统讲解 AI 编码 Agent 在为 .NET 仓库生成单元测试时必须遵守的项目系统识别、构建/测试命令选择、项目引用校验、CS 错误码处理与 MSTest 模板规范。阅读本文后,你将掌握一套可直接落地的 .NET 测试生成决策框架,包括如何区分 SDK-style 与经典非 SDK 项目、如何把新测试文件正确注册进构建系统、如何验证测试能被 CI/评测 harness 发现,以及如何写出与仓库既有框架版本兼容的 MSTest 测试代码。

一、扩展文件在测试生成管线中的定位

dotnet-test插件中,code-testing-extensions 是一个user-invocable: falsedisable-model-invocation: true的引用型技能,它本身不直接执行任何逻辑,而是向测试生成管线提供一份"语言扩展文件清单"。其中 extensions/dotnet.md 就是面向 .NET 的权威语言指南,覆盖构建命令、测试命令、项目引用校验、常见 CS 错误码、MSTest 模板与覆盖率工具策略。

该文件被 code-testing-agent 的多 Agent 管线(Research → Plan → Implement → Build → Test → Fix → Lint)按需加载:code-testing-implementer在写测试前调用code-testing-extensions技能并读取dotnet.md,以确认 .NET 特有的命令与注册规则;unit-test-generation.prompt.md 也明确要求"Project reference validation: Before writing test code, verify the test project references all source projects the tests will use. Call thecode-testing-extensionsskill and read the language-specific extension file for guidance (e.g.,dotnet.mdfor .NET)"。因此,dotnet.md是 .NET 场景下生成测试前必须读入的"操作手册"。

二、项目系统识别:写代码前的第一道决策

dotnet.md的开篇就强调:在选定任何命令或编辑任何清单文件之前,必须先判定项目系统,因为 SDK-style 与经典非 SDK 项目的后续一切操作(构建、测试、文件注册、依赖管理)都完全不同。

信号项目系统后果
根节点<Project Sdk="...">或存在Sdk属性SDK-styledotnet build/dotnet test通常有效;新建的*.cs文件一般会被 glob 通配自动包含
出现ToolsVersionMicrosoft.Common.props/Microsoft.CSharp.targets导入、显式<Reference><Compile Include>经典非 SDK必须保留仓库既有的 MSBuild / 测试运行器命令;每个新增源文件与测试文件都必须手工加入项目
项目旁存在packages.config经典 NuGet 依赖管理必须保留packages.config与程序集引用;除非用户明确要求迁移,否则禁止运行dotnet add package或引入PackageReference

这一判断在仓库测试夹具中有真实案例可循:tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/Discounts.Tests.csproj就是一个典型经典项目,根节点没有Sdk属性,代之以ToolsVersion="15.0"Microsoft.Common.props导入、显式<Reference>(含MSTest.TestFrameworkMoqNBuilderHintPath)以及逐文件列出的<Compile Include>项,并配有packages.config。生成测试时必须对这种项目采用"保留式"策略,而不是试图现代化改造。

对于经典项目,dotnet.md要求以仓库中已检入的脚本、CI 配置、README*AGENTS.md作为构建/测试命令的权威来源。常见命令是MSBuild.exe后接vstest.console.exeMSTest.exe,但"仓库里检入的命令说了算"。如果当前机器没有安装兼容的运行器,应如实报告阻塞原因,而不是迁移项目或谎称dotnet test已成功。

三、构建与测试命令速查

3.1 构建命令

范围命令
SDK-style 测试项目dotnet build MyProject.Tests.csproj
SDK-style 解决方案(最终校验)dotnet build MySolution.sln --no-incremental
经典非 SDK 项目使用仓库既有的 MSBuild 命令(通常是MSBuild.exe MySolution.sln /t:Build

关键约定:

  • 依赖已还原时可加--no-restore跳过还原;
  • -v:q(quiet)减少输出噪音;
  • 最终校验构建必须使用--no-incremental——增量构建会掩盖 CS7036 这类仅在全新编译时暴露的错误。这一点与 unit-test-generation.prompt.md 中"Final full-workspace build: run a full non-incremental build from the workspace root to catch cross-project errors"的要求一致。

3.2 测试命令

范围命令
SDK-style 全部测试dotnet test
SDK-style 按类过滤dotnet test --filter "FullyQualifiedName~ClassName"
SDK-style 构建后运行dotnet test --no-build
经典非 SDK使用仓库检入的运行器命令,常见为 MSBuild 之后执行vstest.console.exe <test.dll>

辅助约定:已构建过则加--no-build;需要安静输出则加-v:q。注意,dotnet.md给出的是基础形态;仓库内 run-tests 技能进一步细化了 VSTest 模式、VSTest-to-MTP 桥接模式与 SDK 10+ 原生 MTP 模式的命令差异,并强调--project只在 SDK 10+ 原生 MTP 命令模式下有效,而桥接模式必须保留--分隔符——这是生成测试后实际运行时的进阶参考。

3.3 格式化(Lint)命令

dotnet format --include path/to/file.cs dotnet format MySolution.sln # 整个解决方案

四、项目引用校验:消灭 CS0234 / CS0246

写测试代码之前,必须读取测试项目的.csproj,确认其中包含测试所依赖程序集的<ProjectReference>项。缺失引用会导致:

  • CS0234("namespace not found")—— 命名空间未找到;
  • CS0246("type not found")—— 类型未找到。

修复方式是补充引用:

<ItemGroup> <ProjectReference Include="../SourceProject/SourceProject.csproj" /> </ItemGroup>

对于经典项目,要保留其既有<ProjectReference>的元数据(如ProjectGUID、Name)与配置映射,而不是替换成 SDK-style 的简写形式。以tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/Discounts.Tests.csproj为例,其引用写法是带<Project>{4560D11B-...}</Project><Name>Discounts</Name>的完整形式,生成测试时不得破坏这种结构。

五、常见 CS 错误码对照表

dotnet.md提供了一张在测试生成与修复循环中高频出现的 C# 编译错误对照表,是code-testing-fixer诊断构建失败时的直接查表依据:

错误码含义修复方法
CS0234命名空间未找到在测试.csproj中为源项目添加<ProjectReference>
CS0246类型未找到添加using Namespace;或补充缺失的<ProjectReference>
CS0103名称未找到检查拼写,添加using语句
CS1061缺少成员核对方法/属性名与源码完全一致
CS0029类型不匹配强转或把类型改成与期望签名一致
CS7036缺少必需参数阅读构造函数/方法签名并传入全部必需参数

以 dotnet-examples.md 中的修复循环为例:error CS0246: The type or namespace name 'Moq' could not be found的根因是测试项目缺少 Moq NuGet 包,修复命令为dotnet add tests/Contoso.Billing.Tests/Contoso.Billing.Tests.csproj package Moq;而error CS7036: There is no argument given that corresponds to the required parameter 'repository'则是因为测试代码对使用主构造函数(primary constructor)的InvoiceService调用了无参构造,正确做法是先创建Mock<IInvoiceRepository>再传入repositoryMock.Object。这两个案例恰好演示了错误码表在"Build → Fix → Rebuild"循环中的实际用法。

六、.csproj/.sln处理与强制注册规则

6.1 构建范围策略

  • 分阶段实现期间,只构建具体测试.csproj以换取速度;
  • 最终校验时用--no-incremental构建完整.sln
  • 全解决方案构建能捕获跨项目引用错误,而作用域受限的构建会漏掉这些错误。

6.2 把测试代码注册进构建(MANDATORY)

写新的 C# 测试文件之前,必须检查测试项目的编译项:

  • SDK-style*.cs通常由 glob 自动包含,除非默认编译项被禁用,否则不要添加冗余的<Compile Include>
  • 经典非 SDK:每个新文件都必须显式登记,路径相对于项目,保留其路径分隔符与排序:
<Compile Include="Services\OrderServiceTests.cs" />

编辑完成后要重新打开项目,确认新测试路径恰好出现一次。磁盘上存在但未登记进经典项目编译项的文件不属于测试程序集,绝不能将其报告为已生成的覆盖率

6.3 注册新测试项目(使用dotnet new后 MANDATORY)

新建的.csprojdotnet test <solution>、仓库根目录下的dotnet test、以及任何 CI/基准测试 harness 都是不可见的,直到它被加入解决方案。必须在创建项目后立即执行dotnet sln add(对应 code-testing-agent 流程中的 Step 3 "Register Test Project with Build System"),绝不能推迟到后续步骤:

  1. 使用研究/计划文档中<TESTAGENT_DIR>下标识的精确解决方案或解决方案筛选器目标(.sln/.slnx/.slnf),不要自行搜索或替换其他目标;
  2. 若目标是.sln.slnx,运行dotnet sln <solution> add <test-project.csproj>
  3. 若目标是.slnf(解决方案筛选器),还要确保新项目被包含进筛选器——只添加到底层.sln可能不足以让测试被发现;
  4. 若项目已包含在测试所用的解决方案或筛选器中,则跳过此步;
  5. 优先使用研究得出的测试命令。只有针对 .NET SDK 10+ 且使用 MTP 风格语法的仓库,才用dotnet test --solution <solution>;否则使用标准位置参数形式dotnet test <solution>

6.4 Harness 发现校验(成功报告的硬门槛)

在报告成功之前,必须从仓库根目录运行与 harness 等价的发现命令,确认测试数量至少增加"你生成的测试数"。harness(CI、msbench、覆盖率工具)并不知道你针对的是哪个.csproj——它运行的是解决方案级命令,所以即使dotnet test MyProject.Tests.csproj能通过,只要dotnet test <solution> --list-tests无法枚举出新测试,就毫无价值:

# 从仓库根目录,针对 <TESTAGENT_DIR>/research.md 中识别的解决方案执行 dotnet test <solution> --list-tests --no-build 2>&1 | grep -c '^ [A-Za-z]'

若增量为0,说明新项目不在解决方案里,执行dotnet sln <solution> add <test-project.csproj>后重新校验。在 harness 命令看到新测试之前,不得报告成功。

对于经典非 SDK 项目,改用仓库的正常构建与发现命令,最低可接受的校验是:

  1. 新文件以<Compile Include="...">的形式恰好出现一次;
  2. 经典项目按其文档化的 MSBuild 命令构建成功;
  3. 仓库的测试运行器能发现新测试。

若环境缺少 Visual Studio/MSBuild/测试运行器工具链,应验证项注册、报告执行被阻塞,不得dotnet test顶替或现代化改造项目。

七、测试框架检测与版本兼容

从测试项目的.csprojpackages.config与引用程序集HintPath检测框架及其安装版本,并匹配仓库既有的框架、模拟库、基础夹具与 API 级别:

包引用框架特性断言风格
MSTest.SdkMSTest.TestFrameworkMSTest[TestClass][TestMethod][DataRow]Assert.AreEqual(expected, actual)
xunitxUnit[Fact][Theory][InlineData]Assert.Equal(expected, actual)
NUnitNUnit[TestFixture][Test][TestCase]Assert.That(actual, Is.EqualTo(expected))

使用仓库既有框架,不要引入不同的框架。

针对 MSTest,只有当安装版本支持对应 API 时才加载writing-mstest-tests的示例。特别是Assert.ThrowsExactly与统一集合断言要求MSTest 3.8+,旧版套件应保持Assert.ThrowsExceptionStringAssertCollectionAssert等兼容写法。绝不为了使用更新示例而升级 MSTest、Moq、NBuilder 或其他测试依赖。这一点与 writing-mstest-tests 中"Treat this as a hard gate"(硬门槛)的规则互为印证:例如 MSTest 3.5.x 不能用Assert.ThrowsExactlyAssert.Contains、ValueTuple 版DynamicData或构造函数注入的TestContextMSTEST0039诊断也提示 3.8+ 才应把Assert.ThrowsException迁移到Assert.Throws/Assert.ThrowsExactly。仓库夹具tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/Discounts.Tests.csproj引用的正是 MSTest.TestFramework 3.5.2,属于必须使用Assert.ThrowsException<T>的老版本场景。

八、MSTest 模板与数据驱动写法

dotnet.md给出了生成测试时的标准 MSTest 模板,包含[TestClass]sealed类、AAA 结构、MethodName_Scenario_ExpectedResult命名与[DataRow]参数化:

using Microsoft.VisualStudio.TestTools.UnitTesting; namespace ProjectName.Tests; [TestClass] public sealed class ClassNameTests { [TestMethod] public void MethodName_Scenario_ExpectedResult() { // Arrange var sut = new ClassName(); // Act var result = sut.MethodName(input); // Assert Assert.AreEqual(expected, result); } [TestMethod] [DataRow(2, 3, 5, DisplayName = "Positive numbers")] [DataRow(-1, 1, 0, DisplayName = "Negative and positive")] public void Add_ValidInputs_ReturnsSum(int a, int b, int expected) { // Act var result = _sut.Add(a, b); // Assert Assert.AreEqual(expected, result); } }

在 dotnet-examples.md 的端到端样例中可以看到这一模板的完整落地:InvoiceServiceTests使用 Moq 模拟IInvoiceRepository,用[DataRow]覆盖CalculateTotal的多个输入组合(含DisplayName),用Assert.ThrowsExactly<ArgumentNullException>/Assert.ThrowsExactlyAsync<KeyNotFoundException>覆盖异常路径,并用_repositoryMock.Verify(r => r.UpdateAsync(invoice), Times.Once)校验状态迁移的副作用——这正是"行为固定"式断言的实践。若要使用 ValueTuple 版DynamicDataTestDataRow<T>(MSTest 3.7+/3.8+)等更进阶的数据驱动写法,可进一步参考 writing-mstest-tests 的 Step 4。

九、覆盖率工具策略:默认不介入

dotnet.md明确要求:默认情况下不要配置或运行代码覆盖率测量工具(coverlet、dotnet-coverage、XPlat Code Coverage),因为这些工具在不同配置下行为不一致且浪费大量时间。覆盖率由评测 harness 另行测量。

例外规则:

  • SDK-style 例外:仅当用户或评测 harness 明确要求 Cobertura/XML 产物时,才以PackageReference形式添加coverlet.collector,让 harness 能产出该产物;
  • 经典非 SDK 项目:保留packages.config,只使用仓库既有的覆盖率工作流,绝不注入PackageReference
  • 无论哪种情况,都不要自己运行覆盖率命令。

这与 dotnet-test/README.md 中"Coverage and CRAP analysis accept existing Cobertura reports; they do not inject SDK-style coverage packages into classic projects"的表述一致。

十、典型工作流串联:从研究到最终报告

把上述规则放入 code-testing-agent 的完整管线中,一条典型的 .NET 测试生成链路如下:

  1. Researchcode-testing-researcher产出<TESTAGENT_DIR>/research.md,记录项目路径、目标框架(如 .NET 9)、测试框架版本(如 MSTest 3.8)、构建/测试/格式化命令、待测文件清单与优先级;
  2. Plancode-testing-planner产出<TESTAGENT_DIR>/plan.md,为每个待测方法规划 happy path、边界与错误用例,并给出分阶段命令(如dotnet build tests/Contoso.Billing.Tests/Contoso.Billing.Tests.csproj);
  3. Implementcode-testing-implementer读取dotnet.md,确认项目系统 → 校验<ProjectReference>→ 按需dotnet sln add→ 生成测试文件 → 执行 scoped build/test/fix 循环;
  4. Harness 校验:按 §6.4 从仓库根目录运行解决方案级--list-tests,确认新测试被枚举;
  5. 最终校验dotnet build MySolution.sln --no-incremental全量构建,跑通所有测试;
  6. Final Reportcode-testing-generator产出汇总报告,列出创建的测试文件、通过/失败数量、覆盖的方法矩阵以及 scoped/full build 的通过状态(详见 dotnet-examples.md 的 Sample Final Report)。

贯穿全程的三条铁律:保留仓库既有项目系统与依赖栈、新文件必须注册进构建、harness 命令看不到的测试等于不存在。遵循这套规范,AI Agent 生成的 .NET 测试才能既编译通过、又真正进入 CI 与评测体系,而非"看起来成功"。

【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 5:31:11

本地LLM构建知识图谱:从笔记到智能关联

1. 项目概述最近在整理个人知识库时&#xff0c;发现了一个有趣的问题&#xff1a;我们积累的笔记、文档和资料越来越多&#xff0c;但它们之间缺乏有效的关联。传统的文件夹分类方式已经无法满足知识管理的需求。于是我开始尝试用本地运行的大语言模型&#xff08;LLM&#xf…

作者头像 李华
网站建设 2026/9/18 5:27:45

OpenClaw 一键安装后模型 Key 怎么填?TaoToken 给 Key 和 Base URL

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 5:27:45

JS逆向实战:从Network断点到Python签名复现

1. 这不是黑客电影&#xff0c;是每个想拿真实数据的人绕不开的实操课“JS逆向”四个字&#xff0c;这两年在爬虫圈里像块试金石——有人看到就头皮发麻&#xff0c;觉得是加密学汇编语言浏览器内核的三重地狱&#xff1b;也有人点开教程两分钟就关掉&#xff0c;因为满屏eval(…

作者头像 李华
网站建设 2026/9/18 5:25:12

Zustand `combine` 中间件完全指南:自动推断类型的状态合并方案

Zustand combine 中间件完全指南&#xff1a;自动推断类型的状态合并方案 【免费下载链接】zustand &#x1f43b; Bear necessities for state management in React 项目地址: https://gitcode.com/gh_mirrors/zu/zustand combine 是 Zustand 官方提供的一个状态创建辅…

作者头像 李华