mcpField
M
migrate-vstest-to-mtp

migrate-vstest-to-mtp

github.com/dotnet测试与质量5.8k3.4k 次安装1 天前更新
代码仓库

关于 migrate-vstest-to-mtp

用于将 .NET 测试解决方案从 VSTest 迁移到 Microsoft.Testing.Platform 的技能,指导配置框架专用运行器、按 SDK 设置 dotnet test、转换命令参数并更新 CI。

主要功能

  1. 01评估测试框架与 SDK 模式
  2. 02配置框架专用的 MTP 运行器
  3. 03按 SDK 版本设置 dotnet test 集成
  4. 04转换 VSTest 参数和筛选器
  5. 05更新 CI 命令并验证结果

适用场景

  1. 01将解决方案中的测试项目迁移至 MTP
  2. 02根据仓库 SDK 调整 dotnet test
  3. 03替换 CI 中的 VSTest 专用命令
  4. 04转换筛选器、覆盖率或崩溃转储选项

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill migrate-vstest-to-mtp

安装后在 Claude Code 中输入/migrate-vstest-to-mtp确认已加载。

详细介绍

VSTest -> Microsoft.Testing.Platform 迁移

将 .NET 测试解决方案从 VSTest 迁移到 Microsoft.Testing.Platform(MTP)。迁移完成后,解决方案中的所有测试项目都将通过 MTP 运行,dotnet test 可正常工作,CI/CD 管道也已更新。

首要操作

在搜索网络或凭记忆作答之前,先检查提供的项目、Directory.Build.props、global.json 和 CI 文件。先确认目标框架和 SDK 模式:.NET 9 及更早版本使用兼容性属性和 -- 分隔符;.NET 10 原生 MTP 使用 global.json,移除该属性,并且传递 MTP 参数时不使用分隔符。对于集中式属性,切勿在 Directory.Build.props 中使用 IsTestProject 作为条件;应使用该文件中已有的属性,例如 MSBuildProjectName。

重要:不要在同一解决方案或运行配置中混用基于 VSTest 和基于 MTP 的 .NET 测试项目——这是不受支持的场景。

适用场景

  • 将任何受支持的测试框架从 VSTest 切换到 Microsoft.Testing.Platform
  • 启用测试项目的 dotnet run / dotnet watch / 直接执行
  • 启用 Native AOT 或修剪后的测试执行
  • 在 MTP 上用 dotnet test 替换 vstest.console.exe
  • 将 CI/CD 管道从 VSTest 任务更新为 .NET Core CLI 任务
  • 将 dotnet test 参数从 VSTest 语法更新为 MTP 语法

不适用场景

  • 项目已通过 Microsoft.Testing.Platform 运行,且无需解决其他 MTP 行为差异(例如,发现的测试数量为 0 时返回退出代码 8)
  • 在不同测试框架之间迁移(例如,从 MSTest 迁移到 xUnit.net)——这是完全不同的工作
  • 项目构建 UWP 或打包的 WinUI 测试项目——MTP 目前尚不支持这些项目
  • 解决方案混用了 .NET 和非 .NET 测试适配器(例如 JavaScript 或 C++ 适配器)——必须使用 VSTest
  • 升级 MSTest 版本——请使用 migrate-mstest-v1v2-to-v3 或 migrate-mstest-v3-to-v4

输入

输入必填说明
项目或解决方案路径否包含测试项目的 .csproj、.sln 或 .slnx 入口点。请自行通过 glob 搜索发现;只有未找到任何文件或选择确实存在歧义时才询问
测试框架否MSTest、NUnit、xUnit.net v2 或 xUnit.net v3。根据包引用自动检测
.NET SDK 版本否决定 dotnet test 集成模式。优先使用仓库的 global.json 或明确指定的 CI SDK;仅当仓库未固定或未注明 SDK 版本时,才使用主机上的 dotnet --version
CI/CD 管道文件否调用 vstest.console 或 dotnet test 的管道定义文件路径

执行与回答约定

  • 在当前工作目录中查找项目、props、global.json 和流水线文件。打开字面搜索结果;技能目录不是用户的代码库。激活技能后继续操作,不要询问可查找到的路径。
  • 对于实现请求,编辑文件,然后验证生效的 MSBuild 属性、转换后的命令、测试数量和所需产物。对于问题,针对检测到的 SDK 和框架提供一条准确的命令/配置,而不是罗列多个近似选项。
  • 保留所有构建参数,避免引入 --no-build、新的设置文件或无关的包升级。除非文件确实存在,否则绝不要保留 --settings <file>。
  • 对于 .NET 9 及更早版本,显示 -- 分隔符。对于 .NET 10 原生 MTP 模式,明确说明要将其移除。
  • 如果有意抑制退出代码 8,请警告:宽泛抑制可能会掩盖因筛选器错误导致的意外空运行;应将其限定在已知的零测试项目/配置中。
  • 对于退出代码 8 的问题,展示以下三种具体形式:--ignore-exit-code 8、TestingPlatformCommandLineArguments 和 TESTINGPLATFORM_EXITCODE_IGNORE=8。
  • 对于 xUnit v3 筛选器,提供可直接使用的 --filter-class/--filter-method/--filter-trait 命令,并解释它们按 AND 逻辑组合。对于已解析的 xUnit 版本支持的情况,还要提供复杂表达式的 --filter-query 路径语法,并链接到 xUnit 查询筛选器文档。
  • 翻译报告器、覆盖率或转储相关内容时,必须在回答中写明每个必需的包;仅提供看似正确的选项而未说明其所属扩展包,属于不完整回答。
  • 最终结果必须说明框架运行器选择加入方式、代码库所选的 SDK 集成模式、准确的转换后命令、扩展包以及验证证据。

工作流

第 1 步:评估解决方案
  1. 识别每个测试项目使用的测试框架——请参阅 platform-detection 技能中的包到框架映射。主要指标:
    • MSTest:引用 MSTest 或 MSTest.TestAdapter,或使用 MSTest.Sdk(且 <IsTestApplication> 未设为 false)。注意:MSTest.TestFramework 单独使用时只是库依赖,不是测试项目。
    • NUnit:引用 NUnit3TestAdapter
    • xUnit.net:引用 xunit 和 xunit.runner.visualstudio
  2. 从 global.json 或用户明确提供的上下文中确定代码库/CI 使用的 SDK。仅当两者都不存在时,才回退到 dotnet --version;不得让 Agent 主机 SDK 悄然覆盖明确指定的 .NET 8/9 CI 目标。
  3. 检查解决方案或代码库根目录下是否存在 Directory.Build.props 文件——为保持一致,所有 MTP 属性都应放在该文件中
  4. 检查 CI 脚本或流水线定义中是否使用 vstest.console.exe
  5. 检查 CI 脚本中是否有 VSTest 专用的 dotnet test 参数:--filter、--logger、--collect、--settings、--blame*
  6. 运行 dotnet test,确定测试通过/失败数量基线
步骤 2:设置 Directory.Build.props

关键:尽可能在解决方案或仓库根目录的 Directory.Build.props 中设置 MTP 运行器属性,而不是按项目设置。这样可以避免配置不一致,例如某些项目使用 VSTest,而其他项目使用 MTP(这是不受支持的场景)。 注意:MTP 还要求测试项目设置 <OutputType>Exe</OutputType>。只有 MSTest.Sdk 会自动设置此项。对于其他所有配置(启用了 EnableMSTestRunner 的 MSTest NuGet 包、启用了 EnableNUnitRunner 的 NUnit、YTest.MTP.XUnit2),建议在 Directory.Build.props 中通过条件集中设置 <OutputType>Exe</OutputType>,且条件仅针对测试项目。如果无法在 Directory.Build.props 中可靠地仅针对测试项目,那么按项目设置 <OutputType>Exe</OutputType> 是可接受的例外。

在 Directory.Build.props 中使用条件:不要使用 Condition="'$(IsTestProject)' == 'true'"——IsTestProject 由测试 SDK targets 在后续求值阶段设置,在导入 Directory.Build.props 时尚不可用。请使用较早可用的属性(例如 MSBuildProjectName),根据命名约定指定测试项目。例如,如果所有测试项目都以 .Tests 结尾:

<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('.Tests'))">
  <OutputType>Exe</OutputType>
</PropertyGroup>

请调整条件(例如 .EndsWith('Tests')、.Contains('.Test')),以匹配仓库使用的测试项目命名约定。

将所有适用的集中属性都放在同一个条件中:OutputType、框架运行器启用属性,以及在 .NET 9 及更早版本中的 TestingPlatformDotnetTestSupport。不要保留仍会影响生产项目的无条件运行器属性。

步骤 3:启用特定框架的 MTP 运行器

每个框架都有自己的启用属性。为保持一致,请在 Directory.Build.props 中添加这些属性。

MSTest

选项 A——MSTest NuGet 包(3.2.0+):

<PropertyGroup>
  <EnableMSTestRunner>true</EnableMSTestRunner>
  <OutputType>Exe</OutputType>
</PropertyGroup>

确保项目引用 MSTest 3.2.0 或更高版本。如果当前版本已是 3.2.0+,迁移到 MTP 无需升级 MSTest 版本。

选项 B——MSTest.Sdk:

使用 MSTest.Sdk 时,默认启用 MTP——无需设置 EnableMSTestRunner 或 OutputType Exe 属性(SDK 会自动设置两者)。唯一需要做的是:如果项目中有 <UseVSTest>true</UseVSTest>,请将其移除。该属性会强制项目使用 VSTest,而不是 MTP。

NUnit

需要 NUnit3TestAdapter 5.0.0 或更高版本。

  1. 将 NUnit3TestAdapter 更新到 5.0.0+:
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
  1. 启用 NUnit 运行器:
<PropertyGroup>
  <EnableNUnitRunner>true</EnableNUnitRunner>
  <OutputType>Exe</OutputType>
</PropertyGroup>
xUnit.net

添加对 YTest.MTP.XUnit2 的引用——此包为 xUnit.net v2 项目提供 MTP 支持,无需升级到 xunit.v3。你还必须将 OutputType 设置为 Exe:

<PackageReference Include="YTest.MTP.XUnit2" Version="0.4.0" />
<PropertyGroup>
  <OutputType>Exe</OutputType>
</PropertyGroup>

注意:YTest.MTP.XUnit2 保留了 VSTest 的 --filter 语法,因此 xUnit.net v2 无需迁移筛选器。它还支持用于 runsettings 的 --settings(仅限 xunit 特定配置)、xunit.runner.json、通过 --report-trx 生成 TRX 报告,以及 --treenode-filter。

xUnit.net v3

xUnit.net v3(xunit.v3 包)内置了 MTP 支持。通过以下方式启用:

<PropertyGroup>
  <UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
</PropertyGroup>

重要:xUnit.net v3 在 MTP 模式下不支持 VSTest 的 --filter 语法。你必须将筛选器转换为 xUnit.net v3 的原生筛选选项(参见步骤 5)。

步骤 4:配置 dotnet test 集成

dotnet test 集成取决于 .NET SDK 版本。

.NET 10 SDK 及更高版本(推荐)

在 global.json 中添加 test 部分,以使用原生 MTP 模式:

{
  "sdk": {
    "version": "10.0.100"
  },
  "test": {
    "runner": "Microsoft.Testing.Platform"
  }
}

在此模式下,dotnet test 参数会直接传递——例如,dotnet test --report-trx。

重要:global.json 不支持尾随逗号。请确保 JSON 严格符合语法。

.NET 9 SDK 及更早版本

使用 dotnet test 命令的 VSTest 模式运行 MTP 测试项目,方法是在 Directory.Build.props 中添加此属性:

<PropertyGroup>
  <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
</PropertyGroup>

重要:在此模式下,必须使用 -- 将 dotnet test 的构建参数与 MTP 参数分隔开。例如:dotnet test --no-build -- --list-tests。

步骤 5:更新 dotnet test 命令行参数

VSTest 专用参数必须转换为对应的 MTP 参数。构建相关参数(-c、-f、--no-build、--nologo、-v 等)保持不变。

VSTest 参数MTP 对应参数备注
--test-adapter-path不适用MTP 不使用外部适配器发现
--blame不适用
--blame-crash--crashdump需要 Microsoft.Testing.Extensions.CrashDump NuGet 包
--blame-crash-dump-type <TYPE>--crashdump-type <TYPE>需要 CrashDump 扩展
--blame-hang--hangdump需要 Microsoft.Testing.Extensions.HangDump NuGet 包
--blame-hang-dump-type <TYPE>--hangdump-type <TYPE>需要 HangDump 扩展
--blame-hang-timeout <TIMESPAN>--hangdump-timeout <TIMESPAN>需要 HangDump 扩展
--collect "Code Coverage;Format=cobertura"--coverage --coverage-output-format cobertura扩展专用参数
-d|--diag <LOG_FILE>--diagnostic
--filter <EXPRESSION>--filter <EXPRESSION>MSTest、NUnit 和 xUnit.net v2(使用 YTest.MTP.XUnit2)语法相同。xUnit.net v3 请参阅下文的筛选器迁移
-l|--logger trx--report-trx需要 Microsoft.Testing.Extensions.TrxReport NuGet 包
--results-directory <DIR>--results-directory <DIR>相同
-s|--settings <FILE>--settings <FILE>MSTest 和 NUnit 仍支持 .runsettings
-t|--list-tests--list-tests相同
-- <RunSettings args>--test-parameter仅适用于 MSTest 和 NUnit
筛选器迁移

MSTest、NUnit 和 xUnit.net v2(使用 YTest.MTP.XUnit2):VSTest 的 --filter 语法在 VSTest 和 MTP 中完全相同,无需更改。

xUnit.net v3(原生 MTP):xUnit.net v3 在 MTP 上不支持 VSTest 的 --filter 语法。必须将筛选器转换为 xUnit.net v3 的原生筛选选项。

xUnit.net v3 筛选标志
标志说明
--filter-class "name"运行指定类中的所有测试。支持通配符(*)。
--filter-not-class "name"排除指定类中的所有测试
--filter-method "name"运行指定的测试方法
--filter-not-method "name"排除指定的测试方法
--filter-namespace "name"运行指定命名空间中的所有测试
--filter-not-namespace "name"排除指定命名空间中的所有测试
--filter-trait "name=value"运行具有匹配 trait 的测试
--filter-not-trait "name=value"排除具有匹配 trait 的测试

单个标志可以指定多个值:--filter-class Foo Bar。

VSTest → xUnit.net v3 筛选器转换表
VSTest --filter 语法xUnit.net v3 MTP 对应参数备注
FullyQualifiedName~ClassName--filter-class *ClassName*子字符串匹配需要使用通配符
FullyQualifiedName=Ns.Class.Method--filter-method Ns.Class.Method按完全限定的方法名精确匹配
Name=MethodName--filter-method *MethodName*子字符串匹配使用通配符
Category=Value(trait)--filter-trait "Category=Value"按 trait 名称/值对筛选
复杂表达式--filter-query "expr"使用 xUnit.net 查询筛选语言(见下文)
xUnit.net v3 查询筛选语言

对于复杂表达式,请使用带路径段语法的 --filter-query:

/<assemblyFilter>/<namespaceFilter>/<classFilter>/<methodFilter>[traitName=traitValue]

每个段分别匹配:程序集名称、命名空间、类名、方法名。任何段中使用 * 表示“匹配全部”。文档:https://xunit.net/docs/query-filter-language

如果 --filter-class、--filter-method 或 --filter-trait 标志与原始筛选条件直接对应,请优先使用它们。只有在使用 --list-tests 验证查询后,才使用 --filter-query;语法上被接受但筛选结果为零个测试的查询,不算成功转换。

转换示例
# VSTest
dotnet test --filter "FullyQualifiedName~IntegrationTests&Category=Smoke"

# xUnit.net v3 MTP -- using individual filters (AND behavior)
dotnet test -- --filter-class *IntegrationTests* --filter-trait "Category=Smoke"

# xUnit.net v3 MTP -- using query language (assembly/namespace/class/method[trait])
dotnet test -- --filter-query "/*/*/*IntegrationTests*/*[Category=Smoke]"

注意:同时使用 --filter-class 和 --filter-trait 时,两个条件必须都匹配(AND 行为)。对于复杂表达式,请使用带路径段语法的 --filter-query。完整参考请参阅 xUnit.net 查询筛选语言文档。

第 6 步:安装 MTP 扩展包(如有需要)

如果 CI 脚本使用 TRX 报告、崩溃转储、挂起转储或覆盖率,请添加相应的包。使用中央包管理时,项目引用如下:

<ItemGroup>
  <PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
  <PackageReference Include="Microsoft.Testing.Extensions.CrashDump" />
  <PackageReference Include="Microsoft.Testing.Extensions.HangDump" />
  <PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
</ItemGroup>

在 Directory.Packages.props 中添加匹配的 PackageVersion 条目。不使用中央包管理时,请在这些引用中填写从已配置源解析出的确切版本。确保版本与所选 MTP 技术栈兼容;不要照搬迁移指南中的固定版本。

第 7 步:更新 CI/CD 流水线
Azure DevOps

如果使用 VSTest 任务(VSTest@3):将其替换为 .NET Core CLI 任务(DotNetCoreCLI@2):

# Before (VSTest task)
- task: VSTest@3
  inputs:
    testAssemblyVer2: '**/*Tests.dll'
    runSettingsFile: 'test.runsettings'

# After (.NET Core CLI task)
- task: DotNetCoreCLI@2
  displayName: Run tests
  inputs:
    command: 'test'
    arguments: '--no-build --configuration Release'

如果已使用 DotNetCoreCLI@2:根据第 5 步的转换更新参数。请记住,.NET 9 及更早版本需要使用 -- 分隔符:

- task: DotNetCoreCLI@2
  displayName: Run tests
  inputs:
    command: 'test'
    arguments: '--no-build -- --report-trx --results-directory $(Agent.TempDirectory)'
GitHub Actions

在工作流文件中更新 dotnet test 调用,使用与第 5 步相同的参数转换。

替换 vstest.console.exe

如果有脚本直接调用 vstest.console.exe,请将其替换为 dotnet test。测试项目现在是可执行文件,也可以直接运行。

第 8 步:处理行为差异
零测试退出代码

发现零个测试时,VSTest 会静默成功。MTP 会返回 退出代码 8。可选方案:

  • 运行测试时传入 --ignore-exit-code 8
  • 在 Directory.Build.props 中添加:
<PropertyGroup>
  <TestingPlatformCommandLineArguments>$(TestingPlatformCommandLineArguments) --ignore-exit-code 8</TestingPlatformCommandLineArguments>
</PropertyGroup>
  • 使用环境变量:TESTINGPLATFORM_EXITCODE_IGNORE=8
第 9 步:移除仅供 VSTest 使用的包(可选)

迁移完成并验证后,移除仅供 VSTest 使用的包:

  • Microsoft.NET.Test.Sdk —— MTP 不需要此包(MSTest.Sdk v4 默认已省略它)
  • xunit.runner.visualstudio —— 仅供 VSTest 发现 xUnit.net 测试时使用(使用 YTest.MTP.XUnit2 时不需要)
  • NUnit3TestAdapter 的 VSTest 专属功能 —— 仍需要此适配器,但仅用于 MTP 运行器

注意:如果过渡期间需要保持与 VSTest 的兼容性,请保留这些包。

第 10 步:验证
  1. 运行 dotnet build —— 确认没有错误
  2. 运行 dotnet test —— 确认所有测试均通过
  3. 将测试通过数和失败数与迁移前的基准进行比较
  4. 直接运行测试可执行文件(例如 ./bin/Debug/net8.0/MyTests.exe)—— 确认其正常工作
  5. 验证 CI 流水线生成预期的测试结果工件(TRX 文件、代码覆盖率、崩溃转储)
  6. 测试 Visual Studio(17.14+)或 VS Code 中的 Test Explorer 是否能发现并运行测试

验证

  • 所有测试项目均使用 MTP 运行器(不再保留仅限 VSTest 的配置)
  • dotnet build 执行完成且无错误
  • dotnet test 通过所有测试,且测试数量与迁移前基线一致
  • 测试可执行文件可直接运行(例如:./bin/Debug/net8.0/MyTests.exe)
  • CI 流水线生成预期的测试结果产物(TRX 文件、代码覆盖率、崩溃转储)
  • Visual Studio 或 VS Code 中的测试资源管理器能够发现并运行测试
  • CI 脚本中不再调用 vstest.console.exe
  • 所有非 MSTest.Sdk 测试项目均设置了 <OutputType>Exe</OutputType>

常见问题

问题解决方案
在同一个解决方案中混用 VSTest 和 MTP 项目一起迁移所有测试项目——不支持混合模式
在 .NET 9 及更早版本中,dotnet test 参数被忽略使用 -- 分隔构建参数与 MTP 参数:dotnet test -- --report-trx
CI 中没有失败却返回退出代码 8MTP 在未运行任何测试时会失败;请使用 --ignore-exit-code 8 或修复测试发现问题
MSTest.Sdk v4 与 vstest.console 不再兼容MSTest.Sdk v4 不再添加 Microsoft.NET.Test.Sdk——请显式添加它,或改用 dotnet test
缺少 <OutputType>Exe</OutputType>除 MSTest.Sdk(会自动设置)外,所有配置都必须设置此项
在 Directory.Build.props 中使用 Condition="'$(IsTestProject)' == 'true'"评估 Directory.Build.props 时尚未定义 IsTestProject——请改用 $(MSBuildProjectName.EndsWith('.Tests'))(或类似的基于名称的检查)

后续步骤

  • 使用 run-tests 在新的 MTP 平台上运行测试
  • 使用 mtp-hot-reload 在 MTP 上通过热重载迭代修复测试

更多信息

评论

支持 Markdown