关于 migrate-mstest-v1v2-to-v3
用于将 MSTest v1 或 v2 测试项目迁移到 v3 的技能,涵盖项目检查、针对性修复及验证,并保留现有测试平台。
主要功能
- 01识别 MSTest v1、v2 和 v3 状态
- 02指导包与设置迁移
- 03将破坏性变更对应到修复方案
- 04提供构建与测试验证指导
适用场景
- 01将 v1 或 v2 测试项目升级到 v3
- 02解决升级包后的源码错误
- 03替换旧版测试设置
- 04检查 DataRow 与断言兼容性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill migrate-mstest-v1v2-to-v3安装后在 Claude Code 中输入/migrate-mstest-v1v2-to-v3确认已加载。
详细介绍
MSTest v1/v2 -> v3 迁移
将测试项目从 MSTest v1(程序集引用)或 MSTest v2(NuGet 1.x-2.x)迁移到 MSTest v3。MSTest v3 与 v1/v2 不具备二进制兼容性——使用 v1/v2 编译的库必须重新编译。
首要操作
在作答前,检查所提供的工作区,并判断项目属于 v1、v2、部分迁移到 v3,还是已完成 v3 迁移。不要先搜索网页或凭记忆作答。对于编辑请求,继续完成所请求的源代码更改和验证;对于建议请求,分类后直接回答。
适用情况
- 项目引用了
Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll(MSTest v1) - 项目使用 MSTest.TestFramework / MSTest.TestAdapter NuGet 1.x 或 2.x
- 将 MSTest 包从 v1/v2 更新到 v3 后解决生成错误——包括包已显示为 3.x,但仍需修复源代码或设置的情况
- 将
.testsettings替换为.runsettings - 采用 MSTest.Sdk 或程序集内并行执行
不适用情况
- 项目已使用 MSTest v3,没有与迁移相关的生成错误,也没有遗留的
.testsettings/<LegacySettings>(迁移已完全完成) - 将 v3 升级到 v4——请使用
migrate-mstest-v3-to-v4 - 在框架之间迁移(从 MSTest 迁移到 xUnit/NUnit)
边界检查
任何编辑之前先检查包版本。如果所有 MSTest 引用都已是 3.x,没有报告 v1/v2 到 v3 的错误,并且没有遗留 .testsettings 或 <LegacySettings>,则说明迁移已完成,不做任何更改。仅包版本为 3.x 并不代表迁移已完成——遗留的 v1/v2 时代设置文件或重大更改错误仍属于处理范围。不要将正常工作的 v3 包合并到元包中。只有在用户要求验证时才运行现有测试。此规则优先于以下所有步骤。
输入
| 输入 | 必需 | 说明 |
|---|---|---|
| 项目或解决方案路径 | 否 | .csproj、.sln 或 .slnx 入口点。搜索工作目录以查找;仅在未找到或多个测试项目导致目标不明确时询问 |
| 生成命令 | 否 | 生成方式(例如 dotnet build、仓库生成脚本)。如未提供,则自动检测 |
| 测试命令 | 否 | 运行测试的方式(例如 dotnet test)。如未提供,则自动检测 |
切勿一开始就询问项目路径。 用户用文字描述其项目是在提问,并非隐瞒文件——请先检查磁盘。如果确实没有项目文件,则根据用户描述的配置作答,不要只回复一个问题。
严格按搜索结果中的路径打开文件。 如果 glob 返回
./TestProject.csproj,说明文件相对于工作目录就在该路径下;请按该路径读取。不要将其拼成相对于此技能自身基础目录的绝对路径:该目录存放的是SKILL.md,而非用户项目,因此会导致读取失败,并误以为项目不存在。如果刚找到的文件无法打开,说明你构造的路径有误——请使用搜索结果中的原始路径重试。只要搜索仍报告项目文件,就绝不能断定“磁盘上没有项目”;也绝不能为了应付文字描述而创建替代项目。
执行约定
- 技能激活并不意味着任务结束。请在同一任务中继续探索工作区并完成请求的工作。
- 技能目录包含指导信息,而不是已暂存的项目。请搜索当前工作目录,打开搜索返回的实际路径;如果某个工具拒绝有效路径,请改用其他可用的读取器或编辑器重试。
- 只要可以通过 glob 或目录搜索找到路径,就不要询问路径。只有在穷尽当前工作区搜索后仍找不到项目,或存在多个项目导致目标确实不明确时,才询问。
- 根据请求的交付内容分类,而非孤立的动词:“修改这些内容”“更新此项目”或“然后构建并运行”意味着执行;“我需要更改什么?”“我应该预期什么?”“步骤相同吗?”或“给我看看”意味着回答,即使提示中也提到升级或迁移。
- 更改文件后,请说明检测到的 MSTest 版本和运行程序、所有已更改的文件,以及确切修复的调用/设置(例如,逐一列出更改为
AreEqual<object>、AreNotEqual<object>或AreSame<object>的断言)。报告干净的测试计数。没有项目证据时,不要声称保留了 VSTest、执行了构建或测试通过。
重大变更摘要
MSTest v3 相较于 v1/v2 引入了以下重大变更。仅处理与项目相关的变更:
| 重大变更 | 影响 | 修复方法 |
|---|---|---|
已移除 Assert.AreEqual(object, object) 重载;仅保留 AreEqual<T>(T?, T?) | CS0411(无法推断类型参数)或 CS1503——但仅当两个参数没有共同的推断类型时才会出现。两个 object 类型的参数仍会推断出 T = object,并且无需修改即可编译 | 在编译失败的调用中添加显式类型参数:Assert.AreEqual<object>(expected, actual)。AreNotEqual、AreSame、AreNotSame 同理。已能编译的断言无需修改 |
DataRow 严格类型匹配 | 不会导致编译错误。 构建时会出现分析器警告 MSTEST0014,并在运行时因“Test data doesn't match method parameters”而失败。扩大转换(int -> long)仍然适用;缩小转换或无关类型(1L -> int、1.0 -> float)则不适用 | 将字面量改为与参数类型完全一致:int 使用 1,long 使用 1L,float 使用 1.0f。运行测试——构建成功并不能证明这里没有问题 |
DataRow 最多支持 16 个参数——仅限 3.0.1 和 3.0.2 | 在这两个版本中会出现 CS1729;3.0.3 起已再次移除该限制 | 在 3.0.3 及更高版本(所有当前 3.x 版本)中,较长的行有效——保持原样即可。不要将多余参数包装进数组、转换为 object 或拆分测试。只有固定使用 3.0.1/3.0.2 的项目需要采取措施:升级到 3.0.3 或更高版本 |
不再支持 .testsettings / <LegacySettings> | 设置会被静默忽略 | 删除 .testsettings,并创建包含等效配置的 .runsettings |
| .NET Core / Framework 的超时行为已统一 | 带有 [Timeout] 的测试可能会出现不同的行为 | 验证超时值;必要时进行调整 |
| 已弃用的目标框架:.NET 5、.NET Fx < 4.6.2、netstandard1.0、UWP < 16299、WinUI < 18362 | 构建错误 | 更新 TFM:将 .NET 5 改为 net8.0(LTS)或 net6.0 及更高版本,将 netfx 改为 net462 及更高版本,将 netstandard1.0 改为 netstandard2.0。注意:net6.0、net8.0、net9.0 均受支持 |
| 与 v1/v2 不兼容二进制兼容 | 针对 v1/v2 编译的库必须重新编译 | 针对 v3 重新编译所有依赖项 |
| 测试 ID 生成方式已变更 | 以测试 ID 为键的播放列表、筛选器或 CI 历史记录可能会重置 | 重新建立 ID 基准,并验证受影响的筛选器 |
TargetInvocationException 不再包装内部异常 | 依赖该包装异常的测试或基础设施会观察到内部异常 | 更新异常处理逻辑,使其预期底层异常 |
| 初始化/清理消息现在会附加到测试结果 | 第一个/最后一个测试的输出可能会新增此前不存在的生命周期消息 | 更新日志处理逻辑,并检查第一个/最后一个测试结果 |
| 所有 TFM 的部署目录行为已统一 | 使用硬编码部署路径的测试可能会失败 | 使用 TestContext.DeploymentDirectory 或已部署项路径,不要依赖假设 |
| 已添加可空注释 | 启用了可空性的项目可能会出现警告 | 修复这些警告,不要抑制无关诊断 |
响应指南
- 始终先确认当前版本:在推荐任何迁移步骤之前,明确说明在项目中检测到的当前 MSTest 版本(例如:“您的项目使用 MSTest v2(2.2.10)”或“这是一个使用 QualityTools 程序集引用的 MSTest v1 项目”)。这样可以让迁移建议有据可依,并确认你已读取项目文件。
- 要求项目证据,但由你自行收集:不要仅凭措辞猜测项目使用 v1 还是 v2——读取项目文件或中央包文件,并将源版本归类为 QualityTools/v1、NuGet 1.x 或 NuGet 2.x。从工作目录中收集这些证据,而不是向用户索取。如果项目已经使用 v3+,且没有遗留的 v1/v2 内容,则停止操作并转到相应技能。
- 保留测试平台:框架升级期间保持 VSTest 或 MTP 不变,除非用户另外要求迁移运行器。
- 执行完整迁移:用户要求迁移或升级项目时,编辑文件、构建项目并运行测试。不要只列出重大变更就停止。只有用户询问预期变化时,才适合仅提供建议。
- 针对性修复请求(用户升级后遇到特定编译错误):仅处理上表中相关的重大变更。简洁展示修复前后的代码。不要完整介绍迁移工作流。
- DataRow 修复请求:将提供的每个
DataRow与其方法签名进行比较。不匹配的内容可能只触发MSTEST0014,但在测试执行期间失败。保留方法契约,并通常修正字面量(1L->1,用于int),然后运行受影响的测试。仅修改确实有误的行。 在 3.0.3+ 中,参数数量本身不构成问题,因此除非编译器报错,否则不要改动长行。 - 不要因猜测而修改——先确认错误:如果你认为某种结构不受支持,应先构建项目并查看实际诊断,再进行修改。如果能够编译,说明此版本支持该结构,无需修改。为了规避项目并未受到的限制而重写有效代码,是缺陷,不是谨慎。
- 特定功能迁移(用户询问
.testsettings、DataRow 或断言等某个方面):仅处理该功能,但要覆盖所提供文件中的每个有效设置或受影响用法。对于.testsettings,将所有 MSTest 设置放在同一个<MSTest>元素下,映射用户要求的部署、每个测试的超时、数据收集器及其他有效配置,不要添加整个会话的超时。不要介绍无关的重大变更。 - “预期变化”问题(用户在升级前询问重大变更):先说明升级到 v3 所需的具体包更新,然后概述《重大变更摘要》中的每个类别,并标明哪些类别直接适用于当前可见项目。每项保持一行,不要展开介绍发行说明历史。
- “预期变化”问题的必需格式:使用基于可见项目的
Applies / Watch / No change表格,并涵盖《重大变更摘要》中的每一行。完整性正是此技能的价值;不要仅为了简洁而省略仅在运行时出现的问题类别。 - 完整迁移请求(用户要求完整迁移):遵循下方的完整工作流。
- 对比问题(用户询问 v1 与 v2 的区别):简洁说明——v1 使用程序集引用,必须先移除这些引用;v2 使用 NuGet,只需更新版本。二者最终都需要使用相同的 v3 包,并面临相同的重大变更。
- 确保执行针对具体项目:修复和完整迁移只修改可见代码或配置中存在的模式。只有用户明确询问“我应该预期哪些变化?”时,才介绍更广泛的内容。
迁移路径
- MSTest v1(程序集引用 QualityTools):移除程序集引用(步骤 2),添加 v3 NuGet 包(步骤 3),修复破坏性变更(步骤 5)。
- MSTest v2(NuGet 包 1.x-2.x):将包版本更新到 3.x(步骤 3),修复破坏性变更(步骤 5)。无需移除程序集引用。
两条路径都在步骤 3 汇合——无论起始版本是什么,均适用相同的 v3 包和破坏性变更。
工作流
步骤 1:评估项目
- 先定位项目:在工作目录中对
*.csproj、*.sln、*.slnx、Directory.Build.props、Directory.Packages.props和*.testsettings执行 glob 搜索。先完成这一步,再询问用户任何问题,并严格按照返回的路径打开对应文件(参见 Inputs 下方的说明)。 - 在一次发现过程中,批量读取项目文件和中央配置文件,搜索受影响的 API/设置,并确定当前使用的 MSTest 版本:
- 程序集引用:在项目引用中查找
Microsoft.VisualStudio.QualityTools.UnitTestFramework-> MSTest v1 - NuGet 包:检查
MSTest.TestFramework和MSTest.TestAdapter包版本 -> 1.x 为 v1,2.x 为 v2
- 程序集引用:在项目引用中查找
- 检查目标框架是否在 v3 中停止支持(参见步骤 4)。
- 运行现有测试命令。记录发现、通过、失败和跳过的数量,作为一致性基线。
步骤 2:移除 v1 程序集引用(如适用)
如果项目通过程序集引用使用 MSTest v1:
- 移除对
Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll的引用- 对于 SDK 样式项目,从
.csproj中移除<Reference>元素 - 对于非 SDK 样式项目,在 Visual Studio 的解决方案资源管理器中依次选择 References -> 右键单击 -> Remove
- 对于 SDK 样式项目,从
- 保存项目文件
步骤 3:将包更新到 MSTest v3
使用一种包模型;不要保留重复的框架/适配器引用。
默认方案——安装 MSTest 元包:
移除单独的 MSTest.TestFramework 和 MSTest.TestAdapter 包引用,并替换为统一的 MSTest 元包:
<PackageReference Include="MSTest" Version="3.8.0" />
如果项目仍使用 VSTest,请保留 Microsoft.NET.Test.Sdk,但将其更新到与所选 MSTest 版本兼容的版本。例如,MSTest 3.8.0 要求 Microsoft.NET.Test.Sdk 版本为 17.13.0 或更高;保留较旧的显式版本会导致 NU1605。如果由中央配置管理包版本,请更新 Directory.Packages.props,而不是添加内联版本。
仅在用户要求或存储库已统一使用 MSTest.Sdk 时使用(仅适用于 SDK 样式项目):
将 <Project Sdk="Microsoft.NET.Sdk"> 更改为 <Project Sdk="MSTest.Sdk/3.8.0">。MSTest.Sdk 会自动提供 MSTest 框架、适配器和分析器。
重要:MSTest.Sdk 默认使用 Microsoft.Testing.Platform(MTP)。如果项目本身必须继续使用 VSTest,请设置
<UseVSTest>true</UseVSTest>。MSTest.Sdk v3 在 MTP 模式下也会提供Microsoft.NET.Test.Sdk,因此,仅仅存在独立的vstest.console调用并不意味着必须更改主要测试运行程序。 不要仅仅因为升级框架而切换测试运行程序。
切换到 MSTest.Sdk 时,移除以下内容(SDK 会自动提供):
- 包:
MSTest、MSTest.TestFramework、MSTest.TestAdapter、MSTest.Analyzers、Microsoft.NET.Test.Sdk - 属性:
<EnableMSTestRunner>、<OutputType>Exe</OutputType>、<IsPackable>false</IsPackable>、<IsTestProject>true</IsTestProject>
第 4 步:按需更新目标框架
MSTest v3 支持 .NET 6+、.NET Core 3.1、.NET Framework 4.6.2+、.NET Standard 2.0、UWP 16299+ 和 WinUI 18362+。.NET Core 3.1 已结束生命周期,但 MSTest v3 仍支持它;在这次仅涉及框架的迁移中请保留它,并建议另行升级运行时。如果项目的目标框架版本已不受 MSTest v3 支持,请更新为受支持的版本:
| 不再支持 | 建议替代版本 |
|---|---|
| .NET 5 | .NET 8.0(当前 LTS)或 .NET 6+ |
| .NET Framework < 4.6.2 | .NET Framework 4.6.2 |
| .NET Standard 1.0 | .NET Standard 2.0 |
| UWP < 16299 | UWP 16299 |
| WinUI < 18362 | WinUI 18362 |
注意:MSTest v3 支持 .NET 6、.NET 8 和 .NET 9。不要更改已受支持的 TFM。
第 5 步:解决构建错误和破坏性变更
先搜索提供的文件,只修复其中实际存在的破坏性变更。 构建成功并不能证明兼容性;有些问题只会以分析器警告的形式出现,或在测试执行期间暴露。
断言重载 -- MSTest v3 将 Assert.AreEqual(object, object) 和 AreNotEqual(object, object) 替换为泛型 AreEqual<T>(T?, T?)。这仅在无法再推断 T 时会导致问题,编译器会报告 CS0411(或在参数类型不相关时报告 CS1503):
// Breaks -- string and int have no common inferred type:
Assert.AreEqual(referenceCode, numericId); // CS0411
// Fix -- name the type argument explicitly:
Assert.AreEqual<object>(referenceCode, numericId);
两个类型为 object 的参数仍会推断出 T = object 并正常编译,无需改动;普通的类型化断言(如 Assert.AreEqual("A-3", order.Reference))也一样。只修复编译器拒绝的调用点。 将文件中的所有断言都扩大为 <object> 也能编译,因此不会触发任何提示,但这会丢弃 v3 新增的类型检查,而这正是此项变更的全部目的。
DataRow 严格类型匹配 -- 参数类型必须与形参类型完全一致。这不是编译错误:该行可以构建(并出现 MSTEST0014),但会在运行时因 "Test data doesn't match method parameters" 而失败。
// Fails at run time: 1L (long) does not bind to an int parameter -> use 1
// Fails at run time: 1.0 (double) does not bind to a float parameter -> use 1.0f
// Still binds: 1 (int) to a long parameter -- widening conversions are accepted
除非形参类型本身有误,否则请保留不变。dotnet build 可能会在出现 MSTEST0014 的情况下成功;请运行测试,确认每一行都能绑定并执行。
参数超过 16 个的行 -- 除非编译器确实发出 CS1729,否则不要修改。这项上限只存在于 3.0.1/3.0.2 中(已在 3.0.3 中移除),因此将额外参数包装在 object[] 中、强制转换为 object 或拆分方法,只会改写原本正确的测试。
超时行为 -- .NET Core 和 .NET Framework 已统一超时行为。请确认 [Timeout] 值仍然有效。
第 6 步:将 .testsettings 替换为 .runsettings
MSTest v3 不再支持 .testsettings 文件和 <LegacySettings>。删除 .testsettings 文件并创建 .runsettings 文件——不要同时保留两者。将所有 MSTest 配置合并到一个 <MSTest> 元素下;不要创建 <MSTestV2> 节。
关键映射:
| .testsettings | 对应的 .runsettings 配置 |
|---|---|
TestTimeout 属性 | <MSTest><TestTimeout>30000</TestTimeout></MSTest> |
| 部署配置 | <MSTest><DeploymentEnabled>true</DeploymentEnabled></MSTest> 或移除 |
| 程序集解析设置 | 移除——现代 .NET 不需要 |
| 数据收集器 | <DataCollectionRunSettings><DataCollectors> 节 |
重要:请将超时映射到
<MSTest><TestTimeout>(针对每个测试),而不是<TestSessionTimeout>(针对整个会话)。请彻底移除<LegacySettings>。
更新所有明确指定旧 .testsettings 路径的项目、CI 命令或 IDE 设置,使其改为指定新的 .runsettings 路径。如果 VSTest 项目必须保留原有行为,但此前从未选中旧文件,请通过 RunSettingsFilePath 使新文件生效。对于 MTP,请使用框架支持的 --settings 路径或现有的 MTP 配置,不要假定 VSTest 的 MSBuild 属性会生效。
步骤 7:验证
- 使用基线测试时相同的测试命令、筛选条件和配置。
dotnet test默认会生成项目;只有在需要单独定位编译失败时,才单独运行生成命令。 - 将发现、通过、失败和跳过的数量与迁移前的基线进行比较。
- 调查每一项数量差异;不要默默接受测试或数据行被丢弃。
- 确认不再存在 QualityTools 引用、1.x/2.x MSTest 包、
.testsettings或<LegacySettings>。
验证
- 正确引用 MSTest v3 包(或 MSTest.Sdk);已移除 v1/v2 引用
- 项目生成成功,错误数为 0
- 所有测试均通过(
dotnet test)——将通过/失败数量与迁移前的基线进行比较 - 已将
.testsettings替换为.runsettings(如适用)
下一步
完成 v3 迁移后,使用 migrate-mstest-v3-to-v4 迁移到 MSTest v4。
常见问题
| 问题 | 解决方案 |
|---|---|
| 工作区中已有项目,却回复“哪个项目?” | 搜索 *.csproj/*.sln/*.slnx,并读取找到的文件 |
| 搜索已列出项目文件,却称“磁盘上没有项目” | 路径是在技能的基目录下重新拼接的。使用搜索结果中的原始路径重新打开;切勿创建替代项目 |
重写含有超过 16 个参数的 DataRow | 3.0.3+ 中有效,当前所有 3.x 版本都支持。只有 3.0.1/3.0.2 曾拒绝此类写法 |
非 MSTest.Sdk 的 VSTest 项目缺少 Microsoft.NET.Test.Sdk | 添加该包引用以支持 VSTest 发现测试 |
| MSTest.Sdk v3 项目必须使用 VSTest 作为主运行器 | 设置 <UseVSTest>true</UseVSTest>;不要仅因为还存在过渡期的 vstest.console 作业就切换运行器 |