mcpField
M
migrate-mstest-v3-to-v4

migrate-mstest-v3-to-v4

github.com/dotnetテスト・品質5.8k3.4k 回インストール1 日前に更新
リポジトリ

migrate-mstest-v3-to-v4 について

MSTest v3 のプロジェクトを v4 に移行するためのスキルです。パッケージ、ソース、実行時、テスト検出の変更を扱い、プロジェクトの状態と依頼内容に応じて案内します。

主な機能

  1. 01MSTest のバージョンと対象フレームワークの確認
  2. 02v4 へのパッケージ更新案内
  3. 03ソース変更に対応するコード修正
  4. 04実行時とテスト検出の問題への対応

活用シーン

  1. 01MSTest v3 プロジェクトの v4 移行
  2. 02更新後のコンパイルエラー修正
  3. 03テスト検出や CI の失敗診断
  4. 04実行時の動作変更の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dotnet/skills --skill migrate-mstest-v3-to-v4

インストール後、Claude Code で/migrate-mstest-v3-to-v4を入力して読み込まれたか確認します。

詳細

MSTest v3 -> v4 の移行

テストプロジェクトを MSTest v3 から MSTest v4 に移行します。移行後は、MSTest v4 を使用するプロジェクトが問題なくビルドされ、テストに合格し、ソース互換性のない変更と動作変更のすべてに対応した状態になります。MSTest v4 は MSTest v3 とバイナリ互換性がありません。v3 に対してコンパイルされたライブラリは、すべて v4 に対して再コンパイルする必要があります。

最初に行うこと

Web を検索したり、記憶を頼りに回答したりする前に、提供されたプロジェクトとソースを調べます。リクエストを、特定のソース修正、ランタイム動作の変更、CI でのテスト検出の問題、互換性に関する質問、または全面的な移行に分類し、以下の該当する行に従います。コンパイルが成功していても、このスキルが不要とは限りません。テスト検出の失敗、TestContext のライフサイクル例外、テスト履歴の変更も、ランタイム移行の失敗です。

使用する場面

  • MSTest.TestFramework、MSTest.TestAdapter、または MSTest メタパッケージを 3.x から 4.x にアップグレードする場合
  • MSTest.Sdk を 3.x から 4.x にアップグレードする場合
  • MSTest v4 パッケージへの更新後に発生したビルドエラーを修正する場合
  • MSTest v4 へのアップグレード後にテスト実行で生じた動作変更を解決する場合
  • v4 に対応するため、カスタムの TestMethodAttribute または ConditionBaseAttribute の実装を更新する場合

使用しない場面

  • プロジェクトがすでに MSTest v4 を使用しており、問題なくビルドできる場合 — 移行は完了しています
  • MSTest v1 または v2 からアップグレードする場合 — 先に migrate-mstest-v1v2-to-v3 を使用し、その後ここに戻ります
  • プロジェクトが MSTest を使用していない場合
  • テストフレームワーク間(例:MSTest から xUnit または NUnit)を移行する場合

入力

入力必須説明
プロジェクトまたはソリューションのパスいいえMSTest のテストプロジェクトを含む .csproj、.sln、または .slnx のエントリポイントです。自分で検索してください。作業ディレクトリを glob で検索し、何も見つからない場合、または選択肢が本当に曖昧な場合に限り、ユーザーに確認します
ビルドコマンドいいえビルド方法(例:dotnet build、リポジトリのビルドスクリプト)です。指定されていない場合は自動検出します
テストコマンドいいえテストの実行方法(例:dotnet test)です。指定されていない場合は自動検出します

結果を左右する判断

検出されたリクエストまたは状態必須の対応
現在のワークスペースにファイルが提供されているその場所を検索し、返されたパスをそのまま開きます。スキルのディレクトリはプロジェクトのディレクトリではありません。ツールの一つで有効なパスを読み取れない場合は、別の利用可能なリーダーまたはエディターで再試行します。自分で見つけられるパスをユーザーに尋ねてはいけません。
ユーザーが提供されたファイルへの変更を依頼している:「プロジェクト/ファイルを修正して」「このソースを更新して」「変更を適用して」「その後ビルドして実行して」影響を受ける箇所をすべて編集します。実際のパッケージバージョンを使い、最も対象を絞った意味のあるビルド/テストコマンドを実行します。スキルの有効化を、助言だけで終える理由にしてはいけません。
ユーザーが「何が起きると想定すればよいか」「これらの変更をどう修正すればよいか」と尋ねている、互換性に関する助言を求めている、または計画を求めているソースが表示されている場合でも、実際のプロジェクトの状態に基づいて直接回答します。単一の症状に関する質問には焦点を絞り、判断を変える隣接リスクのみ含めます。
全面移行でサポート対象外の TFM が使われているまず TFM を更新し、次に MSTest パッケージを更新してから、ソースの変更に対応します。この順序をリリースノートの羅列に埋もれさせてはいけません。
カスタムの TestMethodAttribute サブクラスがあるExecuteAsync、CallerInfo の伝播、表示名の処理、サブクラスの再試行/結果のセマンティクスを、相互に関連する一つの移行として扱います。プレースホルダーの例ではなく、実際のクラスを修正します。
MSTest.Sdk v4 のソース/API エラー(ManagedType、TestTimeout、Contains)がある正確なソース置換を示し、隣接するランナーの警告も追加します。MTP モードでは Microsoft.NET.Test.Sdk が提供されなくなったため、VSTest によるテスト検出が引き続き必要な場合に限り追加します。
MSTest.Sdk v4 と vstest.console を併用しているこれは v4 での変更です。MTP モードでは Microsoft.NET.Test.Sdk が含まれなくなりました。MTP を維持して過渡的な VSTest のテスト検出用にそのパッケージを追加するか、UseVSTest を有効にするか、CI を dotnet test に移行します。選択によってどのランナーが維持されるかを明記します。

回答ガイドライン

  • 必ず最初に現在のバージョンを特定する:移行手順を推奨する前に、プロジェクトで検出された現在の MSTest バージョンを明示してください(例:「このプロジェクトでは MSTest v3(3.8.0)が使用されています」)。これにより、プロジェクトファイルを確認したことが伝わり、移行に関するアドバイスの根拠が明確になります。
  • 推測せず、移行先のバージョンを特定する:ユーザーが「最新」を求めた場合は、プロジェクトで設定されているパッケージソースを照会し、実行時に入手可能な最新の安定版 MSTest v4 を選択してください。このスキル内の例示バージョンを確認せずに結果へコピーしてはいけません。すべての MSTest パッケージを、特定した同じバージョンにそろえてください。
  • 特定の修正依頼(アップグレード後にユーザーが具体的なコンパイルエラーを報告した場合):Step 3 のうち、該当する破壊的変更だけに対応してください。編集を行うのは、依頼された成果物がソースコードの変更である場合に限ります。「これらをどう修正すればよいですか?」は回答の依頼です。必ず、ユーザーが実際に使っている型名とメソッド名を使用した、具体的な修正済みコードを提示してください。 フィクスチャーがまだ v3 を参照している場合、v3 でビルドが成功したことを v4 との互換性の証明として主張してはいけません。パッケージの更新を求められた場合は更新するか、検証の範囲を明示してください。カスタムの TestMethodAttribute サブクラスについては、CallerInfo を base コンストラクターに渡す処理を含む、修正済みのクラス全体を提示してください。事前に検出できた可能性のある関連アナライザー(例:ExpectedException に対する MSTEST0006)にも触れてください。プロジェクトで MSTest.Sdk を使用している場合は、v4 の MTP モードでは Microsoft.NET.Test.Sdk が提供されなくなったことと、それが表示されているランナーに影響するかどうかも明記してください。移行ワークフロー全体の手順を説明してはいけません。
  • 「どんな変更があるか」という質問(アップグレード前に破壊的変更を尋ねられた場合):Step 3 のクイック参照表にある主要な破壊的変更をすべて提示してください。現在のコードに見られるものだけに限定してはいけません。それぞれに、修正方法を一行でまとめてください。さらに、Step 4 の重要な動作変更(特に TestCase.Id の履歴への影響と TreatDiscoveryWarningsAsErrors の既定値)にも触れてください。プロジェクトのコードを参照できる場合は、直接影響する変更を示してください。
  • 完全な移行の依頼(ユーザーが移行全体を求めている場合):以下の完全なワークフローに従ってください。
  • 動作時の症状に関する報告(ビルドエラーを伴わず、テスト実行の違いが説明されている場合):説明された症状を Step 4 の動作変更表に照らし合わせてください。症状に応じた具体的なアドバイスを提示してください。ほかに注意すべき動作変更にも触れてください。ソースコードのビルドエラーも報告されていない限り、ソースの破壊的変更を順に説明してはいけません。
  • CI/テスト検出の問題(アップグレード後にテストが検出されない、vstest.console が動作しなくなった、CI パイプラインが失敗した場合):4.5(MSTest.Sdk v4 は既定の MTP モードで Microsoft.NET.Test.Sdk を含まなくなったため、vstest.console には引き続き必要です)と 4.4(TreatDiscoveryWarningsAsErrors)に焦点を当ててください。根本原因を明確に説明し、次の 3 つすべての方法を提示してください:MTP を維持したまま Microsoft.NET.Test.Sdk を追加する、UseVSTest を設定してプロジェクトのランナーを切り替える、または CI を MTP ネイティブの dotnet test に切り替える。移行ワークフロー全体の手順を説明してはいけません。
  • 説明を求める質問(「これは既知の変更ですか?」「ほかに何に注意すべきですか?」といった質問):関連する変更を説明し、アドバイスしてください。次に遭遇する可能性のある関連変更にも触れてください。移行手順全体を指示してはいけません。
  • 結果の証明:実装作業の最後に、検出した v3 のバージョン、特定した v4 のバージョン、ランナーの選択、変更したファイル、実際のビルド/テスト件数を記載してください。推測からビルド結果、VSTest との互換性、テスト検出、テスト成功を報告してはいけません。

ワークフロー

コミット方針: ユーザーから依頼されない限り、コミットを作成しないでください。パッケージ、ソース、動作の変更は差分内で論理的に分けつつ、依頼された移行を完了し、検証してください。

ステップ 1:プロジェクトを評価する
  1. .csproj、Directory.Build.props、または Directory.Packages.props で MSTest、MSTest.TestFramework、MSTest.TestAdapter、または MSTest.Sdk のパッケージ参照を確認し、現在の MSTest のバージョンを特定します。
  2. プロジェクトが MSTest v3(3.x)を使用していることを確認します。v1 または v2 の場合は、まず migrate-mstest-v1v2-to-v3 を使用してください。
  3. ターゲットフレームワークを確認します。MSTest v4 では、.NET Core 3.1 から .NET 7 までのサポートが終了しています。サポート対象のターゲットフレームワークは、net8.0、net9.0、net462(.NET Framework 4.6.2 以降)、uap10.0.16299(UWP)、net9.0-windows10.0.17763.0(モダン UWP)、および net8.0-windows10.0.18362.0(WinUI)です。
  4. カスタム TestMethodAttribute サブクラスがあるか確認します。v4 では変更が必要です。
  5. ExpectedExceptionAttribute の使用箇所を確認します。v4 では削除されています(v3 以降、アナライザー MSTEST0006 により非推奨)。
  6. Assert.ThrowsException の使用箇所を確認します(非推奨)。v4 では削除されています。
  7. クリーンビルドを実行し、既存のエラーや警告の基準を確認します。
ステップ 2:パッケージを MSTest v4 に更新する

まず、設定済みのパッケージソースから最新の安定版 v4 のバージョンを特定します。メタパッケージ、個別パッケージ、MSTest.Sdk、および一元的なパッケージ管理で、その正確なバージョンを一貫して固定してください。

  • MSTest メタパッケージの場合は、PackageReference を特定した正確なバージョンに更新します。
  • 個別パッケージの場合は、MSTest.TestFramework と MSTest.TestAdapter を同じバージョンに更新します。
  • MSTest.Sdk の場合は、プロジェクトまたは global.json の固定バージョンを同じバージョンに更新します。

dotnet restore を実行し、その後 dotnet build を実行します。ステップ 3 で対応するエラーをすべて収集します。

ステップ 3:ソースコードの破壊的変更を解決する

コンパイルエラーに体系的に対処します。この早見表で該当する変更をすべて特定し、それぞれの修正を適用してください。

コード内のエラー/パターン破壊的変更修正
カスタム TestMethodAttribute が Execute をオーバーライドしているExecute が削除されたTask<TestResult[]> を返す ExecuteAsync に変更します(3.1)
[TestMethod("name")] またはカスタム属性のコンストラクターCallerInfo パラメーターが追加された名前付きパラメーター DisplayName = "name" を使用し、サブクラスでは CallerInfo を引き継ぎます(3.2)
ClassCleanupBehavior.EndOfClass列挙値が削除された引数を削除し、単に [ClassCleanup] とします(3.3)
TestContext.Properties.Contains("key")Properties が IDictionary<string, object> になったContainsKey("key") に変更します(3.4)
[Timeout(TestTimeout.Infinite)]TestTimeout 列挙型が削除された[Timeout(int.MaxValue)] に置き換えます(3.5)
TestContext.ManagedTypeプロパティが削除されたFullyQualifiedTestClassName を使用します(3.6)
Assert.AreEqual(a, b, "msg {0}", arg)メッセージと params を受け取るオーバーロードが削除された文字列補間を使用します:$"msg {arg}"(3.7)
Assert.ThrowsException<T>(...)名前が変更されたAssert.ThrowsExactly<T>(...) または Assert.Throws<T>(...) に置き換えます(3.7)
Assert.IsInstanceOfType<T>(obj, out var t)out パラメーターが削除されたvar t = Assert.IsInstanceOfType<T>(obj) を使用します(3.7)
[ExpectedException(typeof(T))]属性が削除されたテスト本体内にアサーションを移します:Assert.ThrowsExactly<T>(() => ...)(3.8)
プロジェクトのターゲットが net5.0、net6.0、または net7.0TFM のサポートが終了したnet8.0 または net9.0 に変更します(3.9)

重要:修正を始める前に、プロジェクト全体を検索して上記のパターンをすべて確認してください。1 つのプロジェクト内に複数の破壊的変更が含まれていることはよくあります。

3.1 TestMethodAttribute.Execute から ExecuteAsync へ

Execute をオーバーライドするカスタム TestMethodAttribute サブクラスがある場合は、ExecuteAsync に変更します。この変更は、v3 の同期 Execute API では、テストコード内部で async/await を使用するとデッドロックが発生していたため行われました。同期ラッパーがスレッドをブロックする一方で、非同期処理は完了するために同じスレッドを必要としていました。

// Before (v3)
public sealed class MyTestMethodAttribute : TestMethodAttribute
{
    public override TestResult[] Execute(ITestMethod testMethod)
    {
        // custom logic
        return result;
    }
}

// After (v4) -- Option A: wrap synchronous logic with Task.FromResult
public sealed class MyTestMethodAttribute : TestMethodAttribute
{
    public override Task<TestResult[]> ExecuteAsync(ITestMethod testMethod)
    {
        // custom logic (synchronous)
        return Task.FromResult(result);
    }
}

// After (v4) -- Option B: make properly async
public sealed class MyTestMethodAttribute : TestMethodAttribute
{
    public override async Task<TestResult[]> ExecuteAsync(ITestMethod testMethod)
    {
        // custom async logic
        return await base.ExecuteAsync(testMethod);
    }
}

オーバーライドのロジックが完全に同期的な場合は Task.FromResult を使用します。base.ExecuteAsync またはその他の非同期メソッドを呼び出す場合は async/await を使用します。

3.2 TestMethodAttribute の CallerInfo コンストラクター

TestMethodAttribute では、コンストラクターのパラメーターに [CallerFilePath] と [CallerLineNumber] が使用されるようになりました。

TestMethodAttribute を継承している場合は、呼び出し元の情報を基底クラスに渡してください。

public class MyTestMethodAttribute : TestMethodAttribute
{
    public MyTestMethodAttribute(
        [CallerFilePath] string callerFilePath = "",
        [CallerLineNumber] int callerLineNumber = -1)
        : base(callerFilePath, callerLineNumber)
    {
    }
}

サブクラスに独自の表示名コンストラクターがある場合は、その文字列を v4 の基底コンストラクターに渡さないでください。呼び出し元の情報だけを渡し、DisplayName プロパティに値を設定してください。

public sealed class NamedTestMethodAttribute : TestMethodAttribute
{
    public NamedTestMethodAttribute(
        string displayName,
        [CallerFilePath] string callerFilePath = "",
        [CallerLineNumber] int callerLineNumber = -1)
        : base(callerFilePath, callerLineNumber)
    {
        DisplayName = displayName;
    }
}

[TestMethodAttribute("Custom display name")] を使用している場合は、名前付きパラメーターの構文に切り替えてください。

// Before (v3)
[TestMethodAttribute("Custom display name")]

// After (v4)
[TestMethodAttribute(DisplayName = "Custom display name")]
3.3 ClassCleanupBehavior 列挙型の削除

ClassCleanupBehavior 列挙型は削除されました。v3 では、この列挙型によってクラスのクリーンアップをクラスの終了時(EndOfClass)またはアセンブリの終了時(EndOfAssembly)に実行するかを制御していました。v4 では、クラスのクリーンアップは常にクラスの終了時に実行されます。列挙型の引数を削除してください。

// Before (v3)
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void ClassCleanup(TestContext testContext) { }

// After (v4)
[ClassCleanup]
public static void ClassCleanup(TestContext testContext) { }

以前 ClassCleanupBehavior.EndOfAssembly を使用していた場合は、そのクリーンアップロジックを代わりに [AssemblyCleanup] メソッドに移してください。

3.4 TestContext.Properties の型の変更

TestContext.Properties の型が IDictionary から IDictionary<string, object> に変更されました。Contains の呼び出しを ContainsKey に変更してください。

// Before (v3)
testContext.Properties.Contains("key");

// After (v4)
testContext.Properties.ContainsKey("key");
3.5 TestTimeout 列挙型の削除

TestTimeout 列挙型(TestTimeout.Infinite のみを含む)は削除されました。代わりに int.MaxValue を使用してください。

// Before (v3)
[Timeout(TestTimeout.Infinite)]

// After (v4)
[Timeout(int.MaxValue)]
3.6 TestContext.ManagedType の削除

TestContext.ManagedType プロパティは削除されました。代わりに TestContext.FullyQualifiedTestClassName を使用してください。

3.7 Assert API のシグネチャの変更
  • message + params の削除:message と object[] の両方のパラメーターを受け取っていた Assert メソッドは、message のみを受け取るようになりました。書式指定文字列の代わりに文字列補間を使用してください。
// Before (v3)
Assert.AreEqual(expected, actual, "Expected {0} but got {1}", expected, actual);

// After (v4)
Assert.AreEqual(expected, actual, $"Expected {expected} but got {actual}");
  • Assert.ThrowsException の名称変更:Assert.ThrowsException API の名称が変更されました。厳密な型一致には Assert.ThrowsExactly を、派生例外型も受け入れる場合は Assert.Throws を使用してください。
// Before (v3)
Assert.ThrowsException<InvalidOperationException>(() => DoSomething());

// After (v4) -- exact type match (same behavior as old ThrowsException)
Assert.ThrowsExactly<InvalidOperationException>(() => DoSomething());

// After (v4) -- also catches derived exception types
Assert.Throws<InvalidOperationException>(() => DoSomething());
  • Assert.IsInstanceOfType の out パラメーターの変更:Assert.IsInstanceOfType<T>(x, out var t) は var t = Assert.IsInstanceOfType<T>(x) に変更されます。
// Before (v3)
Assert.IsInstanceOfType<MyType>(obj, out var typed);

// After (v4)
var typed = Assert.IsInstanceOfType<MyType>(obj);

この代入への書き換えをすべての箇所に適用し、アサートする具体的な型と、型指定された変数の以降のすべての使用箇所を維持してください。ソースがある場合は、汎用的な MyType の例に置き換えるのではなく、実際のメソッドを表示または編集してから、プロジェクトがコンパイルされることを確認してください。

  • IEquatable<T> に対する Assert.AreEqual の削除:ジェネリック型の推論エラーが発生する場合は、型引数として object を明示的に指定してください。
3.8 ExpectedExceptionAttribute の削除

[ExpectedException] 属性は v4 で削除されました。MSTest 3.2 では、v3 のまま(破壊的変更なしで)Assert.ThrowsExactly への移行を提案し、[ExpectedException] の使用を検出する MSTEST0006 アナライザーが導入されました。v4 では、この属性は完全に削除されています。Assert.ThrowsExactly に移行してください。

// Before (v3)
[ExpectedException(typeof(InvalidOperationException))]
[TestMethod]
public void TestMethod()
{
    MyCall();
}

// After (v4)
[TestMethod]
public void TestMethod()
{
    Assert.ThrowsExactly<InvalidOperationException>(() => MyCall());
}

例外をスローする呼び出しの前にテストのセットアップコードがある場合は、例外をスローする呼び出しだけをラムダで囲み、Arrange/Act の分離を明確に保ってください。

// Before (v3)
[ExpectedException(typeof(ArgumentNullException))]
[TestMethod]
public void Validate_NullInput_Throws()
{
    var service = new ValidationService();
    service.Validate(null);  // throws here
}

// After (v4)
[TestMethod]
public void Validate_NullInput_Throws()
{
    var service = new ValidationService();
    Assert.ThrowsExactly<ArgumentNullException>(() => service.Validate(null));
}

非同期テストメソッドの場合は、Assert.ThrowsExactlyAsync を使用してください。

// Before (v3)
[ExpectedException(typeof(HttpRequestException))]
[TestMethod]
public async Task FetchData_BadUrl_Throws()
{
    await client.GetAsync("https://localhost:0");
}

// After (v4)
[TestMethod]
public async Task FetchData_BadUrl_Throws()
{
    await Assert.ThrowsExactlyAsync<HttpRequestException>(
        () => client.GetAsync("https://localhost:0"));
}

[ExpectedException] で AllowDerivedTypes プロパティを使用していた場合は、Assert.ThrowsExactlyAsync<T>(完全一致)の代わりに Assert.ThrowsAsync<T>(基底型との一致)を使用してください。

対象を絞った移行では、提供されたソース内の属性が付いたすべてのメソッドを変換し、例外が予期される文だけを囲み、準備/セットアップの文はラムダの外に維持してから、影響を受けるテストを実行してください。編集可能なプロジェクトファイルがある場合、API の置き換えを文章で説明するだけでは不十分です。

3.9 対象フレームワークの廃止

MSTest v4 は .NET 8 以降および .NET Framework 4.6.2 以降をサポートします。プラットフォーム固有のサポート対象には uap10.0.16299(UWP)も含まれます。最新の UWP と WinUI では、それぞれに対応するサポート対象の Windows 固有 .NET TFM を使用します。.NET Core 3.1 から .NET 7 はサポート対象外になりました。

テストプロジェクトがサポート対象外のフレームワークを対象としている場合は、TargetFramework を更新してください。


<TargetFramework>net6.0</TargetFramework>


<TargetFramework>net8.0</TargetFramework>
3.10 展開戦略の設定先を TestMethodAttribute に変更

UnfoldingStrategy プロパティ(MSTest 3.7 で導入)は、個々のデータソース属性(DataRowAttribute、DynamicDataAttribute)から TestMethodAttribute に移動しました。

3.11 ConditionBaseAttribute.ShouldRun の名前を変更

ConditionBaseAttribute.ShouldRun プロパティの名前が IsConditionMet に変更されました。

3.12 内部化または削除された型

以前は公開されていたいくつかの型が、内部化または削除されました。

  • MSTestDiscoverer、MSTestExecutor、AssemblyResolver、LogMessageListener
  • TestExecutionManager、TestMethodInfo、TestResultExtensions
  • UnitTestOutcomeExtensions、GenericParameterHelper
  • PlatformServices アセンブリの ITestMethod(TestFramework のものは変更されていません)

コードがこれらのいずれかを参照している場合は、代替手段を探すか、依存関係を削除してください。

ステップ 4:動作の変更に対応する

これらの変更によってビルドエラーは発生しませんが、テストの実行時の動作に影響することがあります。

症状原因対処方法
Azure DevOps でテストが新規として表示される/テスト履歴が失われるTestCase.Id の生成方法が変更された(4.3)コードでの修正は不要です。履歴は再基準化されます
[ClassInitialize] 内で TestContext.TestName を使用すると例外が発生するv4 でライフサイクルのスコープが適用された(4.2)[TestInitialize] またはテストメソッドにアクセスを移動します
テストが検出されない/検出に失敗するTreatDiscoveryWarningsAsErrors が true になった(4.4)警告を修正するか、.runsettings で false に設定します
以前は発生しなかったテストのハングが起きるAppDomain がデフォルトで無効になった(4.1).runsettings の RunConfiguration で DisableAppDomain を false に設定します
v4 へのアップグレード後、vstest.console が MSTest.Sdk のテストを見つけられないMSTest.Sdk は MTP をデフォルトとし、v4 では MTP モードで Microsoft.NET.Test.Sdk が追加されなくなった(4.5)MTP を維持したまま明示的にパッケージを追加するか、UseVSTest を設定するか、CI を dotnet test に切り替えます
アナライザーから新しい警告が出るアナライザーの重大度が引き上げられた(4.6)警告を修正するか、.editorconfig で抑制します
4.1 DisableAppDomain のデフォルトが true に変更

AppDomain はデフォルトで無効です。.NET Framework で testhost 内(dotnet test および VS のデフォルト)で実行する場合、MSTest は AppDomain を自動的に再有効化します。AppDomain の分離を明示的に制御する必要がある場合は、.runsettings で設定してください。

<RunSettings>
  <RunConfiguration>
    <DisableAppDomain>false</DisableAppDomain>
  </RunConfiguration>
</RunSettings>
4.2 TestContext を誤ったタイミングで使用すると例外が発生

MSTest v4 では、ライフサイクルの誤った段階でテスト固有のプロパティにアクセスすると、例外が発生するようになりました。

  • TestContext.FullyQualifiedTestClassName -- [AssemblyInitialize] 内ではアクセスできません
  • TestContext.TestName -- [AssemblyInitialize] または [ClassInitialize] 内ではアクセスできません

対処方法:[ClassInitialize] から TestContext.TestName にアクセスするコードを、[TestInitialize] または個々のテストメソッドに移動してください。これらの場所では、テストごとのコンテキストを利用できます。回避策として TestName を FullyQualifiedTestClassName に置き換えないでください。両者の意味は異なります。

4.3 TestCase.Id の生成方法が変更

長年のバグを修正するため、TestCase.Id の生成アルゴリズムが変更されました。Azure DevOps のテスト結果の追跡(例:テスト失敗の経時的な追跡)に影響する場合があります。コードでの修正は不要ですが、テスト結果の履歴が連続しなくなる点にご注意ください。

4.4 TreatDiscoveryWarningsAsErrors のデフォルトが true に変更

v4 では、より厳格なデフォルト設定が使用されます。検出時の警告がエラーとして扱われるようになったため、以前は検出に問題があっても実行されていたテストが、まったく実行されなくなる場合があります。アップグレード後に予期しないテストの失敗(ビルドエラーではなく、テストが検出されない)が発生した場合は、検出時の警告を確認してください。調査中に v3 の動作に戻すには、次のようにします。

<RunSettings>
  <MSTest>
    <TreatDiscoveryWarningsAsErrors>false</TreatDiscoveryWarningsAsErrors>
  </MSTest>
</RunSettings>

推奨:この設定を抑制するのではなく、根本原因となっている検出時の警告を修正してください。

4.5 MSTest.Sdk と vstest.console の互換性

MSTest.Sdk は、既定で Microsoft.Testing.Platform(MTP)モードになります。MSTest.Sdk v3 では、このモードでも Microsoft.NET.Test.Sdk が追加されていましたが、v4 では不要な参照が削除されています。そのため、vstest.console を別途実行する CI パイプラインでは、v4 にアップグレードした直後に検出されるテストがゼロになることがあります。

オプション A — MTP と移行期間中の VSTest による検出を維持する:互換性のある Microsoft.NET.Test.Sdk パッケージを正確なバージョンで明示的に追加します。MTP を引き続き主要なランナーとして使いつつ、既存の vstest.console ジョブをまだ削除できない場合に、最も影響の少ない修正方法です。

構成済みフィードから解決された互換性のある正確なバージョンを指定して、直接 PackageReference を使用します。Central Package Management を使用している場合は、Directory.Packages.props の Microsoft.NET.Test.Sdk PackageVersion を追加または更新し、プロジェクト参照ではバージョンを指定しないようにします。固定されたサンプルバージョンをコピーしないでください。

実際の vstest.console コマンドで検証してください。dotnet test による MTP 実行が成功しても、VSTest でテストが検出されることの証明にはなりません。

オプション B — プロジェクトを VSTest モードに切り替える:UseVSTest プロパティを設定します。これにより、MSTest.Sdk が Microsoft.NET.Test.Sdk を追加します。

<PropertyGroup>
  <UseVSTest>true</UseVSTest>
</PropertyGroup>

手順 2 で解決した正確な MSTest.Sdk v4 のバージョン指定はそのまま維持してください。このオプションで変更するのはランナーであり、選択した MSTest のバージョンやターゲットフレームワークではありません。

オプション C — CI を dotnet test に切り替える:CI パイプラインでの vstest.console の呼び出しを dotnet test に置き換えます。これは MTP でネイティブに動作し、MSTest.Sdk プロジェクトでは長期的に推奨される方法です。

この動作が v4 より前からあったと説明しないでください。MTP モードで推移的な Microsoft.NET.Test.Sdk 参照が削除されたことは、v4 の動作上の破壊的変更の一つです。

4.6 アナライザーの重大度の変更

複数のアナライザーの既定の重大度が Info から Warning に引き上げられました。

  • MSTEST0001、MSTEST0007、MSTEST0017、MSTEST0023、MSTEST0024、MSTEST0025
  • MSTEST0030、MSTEST0031、MSTEST0032、MSTEST0035、MSTEST0037、MSTEST0045

新たに発生した警告を確認して修正するか、意図したものであれば .editorconfig で抑制してください。

手順 5:検証
  1. dotnet build を実行し、エラーがゼロであることを確認して、新たに発生した警告を確認します
  2. dotnet test を実行し、すべてのテストが成功することを確認します
  3. テスト結果(成功数/失敗数)を移行前のベースラインと比較します
  4. Azure DevOps のテスト追跡を使用している場合は、TestCase.Id の変更が履歴の継続性に影響する可能性があることに注意してください
  5. 検出条件の厳格化によって、テストがひそかに除外されていないことを確認します

検証

  • すべての MSTest パッケージが 4.x に更新されている
  • プロジェクトがエラーゼロでビルドできる
  • dotnet test ですべてのテストが成功する
  • カスタム TestMethodAttribute サブクラスが ExecuteAsync と CallerInfo に対応するよう更新されている
  • ExpectedExceptionAttribute が Assert.ThrowsExactly に置き換えられている
  • Assert.ThrowsException が Assert.ThrowsExactly(または Assert.Throws)に置き換えられている
  • ClassCleanupBehavior 列挙型の使用箇所が削除されている
  • TestContext.Properties.Contains が ContainsKey に更新されている
  • すべてのターゲットフレームワークが net8.0 以降、net9.0、net462 以降、uap10.0.16299、または WinUI である
  • 動作の変更が確認され、対処されている
  • 移行中にテストが失われていない(テスト数を比較する)

関連スキル

  • writing-mstest-tests — 最新の MSTest v4 アサーション API とテスト作成のベストプラクティス
  • run-tests — 移行後のテスト実行

よくある落とし穴

落とし穴解決策
カスタムの TestMethodAttribute が引き続き Execute をオーバーライドしているTask<TestResult[]> を返す ExecuteAsync に変更します
TestMethodAttribute("display name") がコンパイルできなくなったTestMethodAttribute(DisplayName = "display name") を使用します
ClassCleanupBehavior 列挙型が見つからない列挙型の引数を削除します。[ClassCleanup] はクラスの最後に常に実行されるようになりました。アセンブリの最後にクリーンアップするには、[AssemblyCleanup] を使用します
TestContext.Properties.Contains が見つからないContainsKey を使用します。Properties は IDictionary<string, object> になりました
ExpectedException 属性が見つからないテスト本体内で Assert.ThrowsExactly<T>(() => ...) に置き換えます
Assert.ThrowsException が見つからないAssert.ThrowsExactly(派生型の場合は Assert.Throws)に置き換えます
書式文字列の引数を指定した Assert.AreEqual が失敗する文字列補間を使用します:$"message {value}"
以前はハングしなかったテストがハングするAppDomain は既定で無効です。testhost の .NET Fx では自動的に再有効化されます
Azure DevOps のテスト履歴が壊れる想定どおりです。TestCase.Id の生成方法が変更されました。コードでの修正は不要で、結果は再ベースライン化されます
検出時の警告によって実行が失敗するようになったTreatDiscoveryWarningsAsErrors は既定で true です。検出時の警告を修正してください
Net6.0/net7.0 のターゲットがコンパイルできないnet8.0 に更新します。MSTest v4 は net8.0、net9.0、net462、uap10.0.16299、最新の UWP、WinUI をサポートします

コメント