この記事では、Microsoft用のカスタム テスト フレームワークを作成する方法について説明します。Testing.Platform (MTP)。 テスト フレームワークは、唯一の必須拡張機能です。 テストを検出して実行し、結果をプラットフォームに報告します。
完全な拡張ポイントの概要とプロセス内/プロセス外の概念については、「 カスタム拡張機能の作成」を参照してください。
既存の VSTest ベースのテスト フレームワークを移行する場合は、 ITestFramework インターフェイスをネイティブに実装することをお勧めします。
VSTest Bridge 拡張機能は移行手順として使用できますが、ネイティブ実装では最適なエクスペリエンスが提供されます。
テストフレームワークの拡張
テスト フレームワークは、テスト プラットフォームにテストを検出して実行する機能を提供する主要な拡張機能です。 テスト フレームワークは、テストの結果をテスト プラットフォームに戻す役割を担います。 テスト フレームワークは、テスト セッションを実行するために必要な唯一の必須の拡張機能です。
テスト フレームワークを登録する
このセクションでは、テスト フレームワークをテスト プラットフォームに登録する方法について説明します。
TestApplication.RegisterTestFrameworkのドキュメントに示すように、 API を使用して、テスト アプリケーション ビルダーごとに 1 つのテスト フレームワークのみを登録します。
登録 API は次のように定義されます。
ITestApplicationBuilder RegisterTestFramework(
Func<IServiceProvider, ITestFrameworkCapabilities> capabilitiesFactory,
Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework> adapterFactory);
RegisterTestFramework API には、次の 2 つのファクトリが必要です。
Func<IServiceProvider, ITestFrameworkCapabilities>: これは、IServiceProviderインターフェイスを実装するオブジェクトを受け入れ、ITestFrameworkCapabilitiesインターフェイスを実装するオブジェクトを返すデリゲートです。IServiceProviderは、構成、ロガー、コマンド ライン引数といったプラットフォーム サービスへのアクセスを提供します。ITestFrameworkCapabilitiesインターフェイスは、テスト フレームワークでサポートされる機能をプラットフォームと拡張機能に通知するために使用されます。 これにより、特定の動作を実装およびサポートすることで、プラットフォームと拡張機能は適切に対話できるようになります。 機能の概念について理解を深めるには、それぞれのセクションを参照してください。Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework>: これは、ITestFrameworkCapabilities オブジェクトを受け取るデリゲートであり、このオブジェクトはFunc<IServiceProvider, ITestFrameworkCapabilities>によって返されるインスタンスです。また、IServiceProvider を使用してプラットフォーム サービスへのアクセスを再提供します。 予期される戻りオブジェクトは、ITestFramework インターフェイスを実装するオブジェクトです。ITestFrameworkは、テストを検出して実行してから結果をテスト プラットフォームに伝える実行エンジンとして機能します。
プラットフォームが ITestFrameworkCapabilities の作成と ITestFramework の作成を分離する必要があるのは、サポートされている機能が現在のテスト セッションを実行するのに十分でない場合に、テスト フレームワークの作成を回避するよう最適化するためです。
次のユーザー コードの例を考えてみましょう。この例は、空の機能セットを返すテスト フレームワークの登録を示しています。
internal class TestingFrameworkCapabilities : ITestFrameworkCapabilities
{
public IReadOnlyCollection<ITestFrameworkCapability> Capabilities => [];
}
internal class TestingFramework : ITestFramework
{
public TestingFramework(ITestFrameworkCapabilities capabilities, IServiceProvider serviceProvider)
{
// ...
}
// Omitted for brevity...
}
public static class TestingFrameworkExtensions
{
public static void AddTestingFramework(this ITestApplicationBuilder builder)
{
builder.RegisterTestFramework(
_ => new TestingFrameworkCapabilities(),
(capabilities, serviceProvider) => new TestingFramework(capabilities, serviceProvider));
}
}
// ...
次に、登録コードを使用して、この例の対応するエントリ ポイントについて考えてみましょう。
var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args);
// Register the testing framework
testApplicationBuilder.AddTestingFramework();
using var testApplication = await testApplicationBuilder.BuildAsync();
return await testApplication.RunAsync();
注
空の ITestFrameworkCapabilities を返しても、テスト セッションの実行が妨げられることはありません。 すべてのテスト フレームワークは、テストを検出して実行できる必要があります。 その影響は、テスト フレームワークに特定の機能がない場合にオプトアウトする可能性がある拡張機能に限定する必要があります。
フレームワーク固有のメタデータが必要なレポートについては、ITrxReportCapability を登録し、テスト結果に対応する TRX プロパティを追加してください。
テスト フレームワークを作成する
Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework は、テスト フレームワークを提供する拡張機能によって実装されます。
public interface ITestFramework : IExtension
{
Task<CreateTestSessionResult> CreateTestSessionAsync(CreateTestSessionContext context);
Task ExecuteRequestAsync(ExecuteRequestContext context);
Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context);
}
IExtension インターフェイス
ITestFramework インターフェイスは、IExtension インターフェイスを継承します。これは、すべての拡張ポイントが継承するインターフェイスです。
IExtension は、拡張機能の名前と説明を取得するために使用されます。 また IExtension は、Task<bool> IsEnabledAsync() を通じて、セットアップで拡張機能を動的に有効または無効にする方法も提供します。 無効にする特定の要件がない場合は、このメソッドから true を返すようにしてください。
CreateTestSessionAsync メソッド
CreateTestSessionAsync メソッドはテスト セッションの開始時に呼び出され、テスト フレームワークの初期化に使用されます。 API は CreateTestSessionContext オブジェクトを受け入れ、CreateTestSessionResult を返します。
public sealed class CreateTestSessionContext : TestSessionContext
{
public CancellationToken CancellationToken { get; }
}
SessionUid プロパティは、TestSessionContextから継承されます (「TestSessionContext」セクションを参照)。
CancellationToken を使用して、CreateTestSessionAsync の実行を停止します。
戻りオブジェクトは、次のように CreateTestSessionResult になります。
public sealed class CreateTestSessionResult
{
public string? WarningMessage { get; set; }
public string? ErrorMessage { get; set; }
public bool IsSuccess { get; set; }
}
IsSuccess プロパティは、セッションの作成が成功したかどうかを示すために使用されます。
false が返されると、テストの実行は停止します。
CloseTestSessionAsync メソッド
CloseTestSessionAsync メソッドは機能的には CreateTestSessionAsync と並列で、オブジェクト名だけが異なります。 詳細については、「CreateTestSessionAsync」のセクションを参照してください。
ExecuteRequestAsync メソッド
ExecuteRequestAsync メソッドは、型 ExecuteRequestContext のオブジェクトを受け入れます。 このオブジェクトには、名前からもわかるように、テスト フレームワークでの実行が想定されているアクションの詳細が格納されています。
ExecuteRequestContext の定義は次のとおりです。
public sealed class ExecuteRequestContext
{
public IRequest Request { get; }
public IMessageBus MessageBus { get; }
public CancellationToken CancellationToken { get; }
public void Complete();
}
IRequest: これは、任意の種類の要求に対応した基本インターフェイスです。 テスト フレームワークは、次のようなライフサイクルのインプロセス ステートフル サーバーと考える必要があります。
上の図は、テスト フレームワーク インスタンスの作成後にテスト プラットフォームが 3 つの要求を発行することを示しています。 テスト フレームワークはこれらの要求を処理し、要求自体に含まれている IMessageBus サービスを利用して、特定の要求ごとの結果を提供します。 特定の要求が処理されると、テスト フレームワークはその要求に対して Complete() メソッドを呼び出し、要求が実行されたことがテスト プラットフォームに通知されます。
テスト プラットフォームは、ディスパッチされたすべての要求を監視します。 すべての要求が実行されると、インスタンスの CloseTestSessionAsync を呼び出して破棄します (IDisposable/IAsyncDisposable が実装されている場合)。
要求とその完了が重複して、要求の同時実行と非同期実行が有効になる可能性があることは明らかです。
注
現在、テスト プラットフォームでは重複する要求は送信されず、次の要求を送信する前に要求 >> の完了を待ちます。 ただし、この動作は、今後変更される可能性があります。 同時要求のサポートは、機能システムを通じて決定されます。
IRequest の実装により、実行する必要のある正確な要求が指定されます。 テスト フレームワークは要求の種類を識別し、それに応じて処理します。 要求の種類が認識されない場合は、例外が発生します。
使用可能な要求の詳細については、IRequest セクションを参照してください。
IMessageBus: このサービスは、要求にリンクされているため、テスト フレームワークは、進行中の要求に関する情報を非同期でテスト プラットフォームに発行できます。
メッセージ バスはプラットフォームの中央ハブとして機能し、すべてのプラットフォーム コンポーネントと拡張機能間の非同期通信を支援します。
テスト プラットフォームに公開される可能性のある情報の包括的な一覧については、IMessageBus セクションを参照してください。
CancellationToken: このトークンは、特定の要求の処理を中断するために使用されます。
結果の公開やクリーンアップをキャンセルせずに新しいテストのスケジュールを停止するには、IGracefulStopTestExecutionCapabilityを実装してください。 この独立した機能により、--maximum-failed-tests などの機能がサポートされています。
Complete(): 前のシーケンスで示したように、Complete メソッドは、要求が正常に処理され、関連するすべての情報が IMessageBus に送信されたことをプラットフォームに通知します。
Warnung
要求で Complete() の呼び出しを無視すると、テスト アプリケーションが応答しなくなります。
テスト フレームワークを自分自身の要件またはユーザーの要件に合わせてカスタマイズするには、構成ファイル内のパーソナライズされたセクションを使用するか、カスタム コマンド ライン オプションを使用します。
要求の処理
次のセクションでは、テスト フレームワークが受信して処理する可能性のあるさまざまな要求について詳しく説明します。
次のセクションに進む前に、テスト実行情報をテスト プラットフォームに伝えるための重要なサービス、IMessageBus の概念について十分理解することが重要です。
TestSessionContext
TestSessionContext はすべての要求の共有プロパティで、進行中のテスト セッションに関する情報を提供します。
public class TestSessionContext
{
public SessionUid SessionUid { get; }
}
public readonly struct SessionUid(string value)
{
public string Value { get; }
}
TestSessionContext は、進行中のテスト セッションの一意識別子である SessionUid で構成され、テスト セッション データのログ作成と関連付けを支援します。
DiscoverTestExecutionRequest(テスト実行リクエストの検出)
public class DiscoverTestExecutionRequest
{
public TestSessionContext Session { get; }
public ITestExecutionFilter Filter { get; }
}
DiscoverTestExecutionRequest は、テスト フレームワークにテストを検出し、この情報を IMessageBus に伝えるよう指示します。
前のセクションで説明したように、検出されたテストのプロパティは DiscoveredTestNodeStateProperty です。 参照用の汎用コード スニペットを次に示します。
var testNode = new TestNode
{
Uid = GenerateUniqueStableId(),
DisplayName = GetDisplayName(),
Properties = new PropertyBag(
DiscoveredTestNodeStateProperty.CachedInstance),
};
await context.MessageBus.PublishAsync(
this,
new TestNodeUpdateMessage(
discoverTestExecutionRequest.Session.SessionUid,
testNode));
// ...
テスト実行要求を実行
public class RunTestExecutionRequest
{
public TestSessionContext Session { get; }
public ITestExecutionFilter Filter { get; }
}
RunTestExecutionRequest は、テスト フレームワークにテストを実行し、この情報を IMessageBus に伝えるよう指示します。
参照用の汎用コード スニペットを次に示します。
var skippedTestNode = new TestNode()
{
Uid = GenerateUniqueStableId(),
DisplayName = GetDisplayName(),
Properties = new PropertyBag(
SkippedTestNodeStateProperty.CachedInstance),
};
await context.MessageBus.PublishAsync(
this,
new TestNodeUpdateMessage(
runTestExecutionRequest.Session.SessionUid,
skippedTestNode));
// ...
var successfulTestNode = new TestNode()
{
Uid = GenerateUniqueStableId(),
DisplayName = GetDisplayName(),
Properties = new PropertyBag(
PassedTestNodeStateProperty.CachedInstance),
};
await context.MessageBus.PublishAsync(
this,
new TestNodeUpdateMessage(
runTestExecutionRequest.Session.SessionUid,
successfulTestNode));
// ...
var assertionFailedTestNode = new TestNode()
{
Uid = GenerateUniqueStableId(),
DisplayName = GetDisplayName(),
Properties = new PropertyBag(
new FailedTestNodeStateProperty(assertionException)),
};
await context.MessageBus.PublishAsync(
this,
new TestNodeUpdateMessage(
runTestExecutionRequest.Session.SessionUid,
assertionFailedTestNode));
// ...
var failedTestNode = new TestNode()
{
Uid = GenerateUniqueStableId(),
DisplayName = GetDisplayName(),
Properties = new PropertyBag(
new ErrorTestNodeStateProperty(ex.InnerException!)),
};
await context.MessageBus.PublishAsync(
this,
new TestNodeUpdateMessage(
runTestExecutionRequest.Session.SessionUid,
failedTestNode));
要求フィルターを適用する
ユーザーまたはツールが選択したテストのみを実行するには、検出と実行の両方に TestExecutionRequest.Filter を適用します。 MTP はフィルターをフレームワークに渡します。プラットフォームでは、フレームワークが発行または実行するテストはフィルター処理されません。
Microsoft.Testing.Platform.Requestsのstable フィルター コントラクトは次のとおりです。
| Filter | フレームワークの動作 |
|---|---|
NopFilter |
追加の制約を適用しません。 |
TestNodeUidListFilter |
安定した TestNodeUid が TestNodeUids に含まれるテストのみを選択します。 空の配列では、テストは選択されません。 |
UID を、内容を解釈しない大文字と小文字を区別する識別子として比較します。 表示名や新しく生成された識別子に置き換えないでください。 このプラットフォームでは、--filter-uid 用の UID フィルター、対象を絞ったツール要求、および 再試行拡張機能 が使用されています。
次のヘルパーは、これら 2 つのフィルターの種類を処理し、完全なスイートをサイレント実行するのではなく、サポートされていないフィルターを拒否します。
private static bool MatchesFilter(ITestExecutionFilter filter, TestNodeUid uid)
=> filter switch
{
NopFilter => true,
TestNodeUidListFilter uidFilter => uidFilter.TestNodeUids.Any(
candidate => string.Equals(candidate.Value, uid.Value, StringComparison.Ordinal)),
_ => throw new NotSupportedException(
$"Filter '{filter.GetType().FullName}' isn't supported.")
};
Private Shared Function MatchesFilter(filter As ITestExecutionFilter, uid As TestNodeUid) As Boolean
If TypeOf filter Is NopFilter Then
Return True
End If
Dim uidFilter = TryCast(filter, TestNodeUidListFilter)
If uidFilter IsNot Nothing Then
Return uidFilter.TestNodeUids.Any(
Function(candidate) String.Equals(candidate.Value, uid.Value, StringComparison.Ordinal))
End If
Throw New NotSupportedException($"Filter '{filter.GetType().FullName}' isn't supported.")
End Function
検出されたノードを発行するか、選択したテストを実行する前に、ヘルパーを使用します。 次のサンプル フレームワークでは、両方の要求に同じ選択ロジックを使用します。
public async Task ExecuteRequestAsync(ExecuteRequestContext context)
{
if (context.Request is not DiscoverTestExecutionRequest
and not RunTestExecutionRequest)
{
throw new NotSupportedException("The request type isn't supported.");
}
var request = (TestExecutionRequest)context.Request;
foreach (var test in s_tests)
{
context.CancellationToken.ThrowIfCancellationRequested();
var uid = new TestNodeUid(test.Uid);
if (!MatchesFilter(request.Filter, uid))
{
continue;
}
if (request is DiscoverTestExecutionRequest)
{
await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(
request.Session.SessionUid,
new TestNode
{
Uid = uid,
DisplayName = test.Name,
Properties = new PropertyBag(DiscoveredTestNodeStateProperty.CachedInstance)
}));
}
else
{
await PublishResultAsync(context, request, uid, test.Name);
}
}
context.Complete();
}
Public Async Function ExecuteRequestAsync(context As ExecuteRequestContext) As Task Implements ITestFramework.ExecuteRequestAsync
If Not TypeOf context.Request Is DiscoverTestExecutionRequest AndAlso
Not TypeOf context.Request Is RunTestExecutionRequest Then
Throw New NotSupportedException("The request type isn't supported.")
End If
Dim request = DirectCast(context.Request, TestExecutionRequest)
For Each test In s_tests
context.CancellationToken.ThrowIfCancellationRequested()
Dim uid As New TestNodeUid(test.Uid)
If Not MatchesFilter(request.Filter, uid) Then
Continue For
End If
If TypeOf request Is DiscoverTestExecutionRequest Then
Await context.MessageBus.PublishAsync(Me, New TestNodeUpdateMessage(
request.Session.SessionUid,
New TestNode With {
.Uid = uid,
.DisplayName = test.Name,
.Properties = New PropertyBag(DiscoveredTestNodeStateProperty.CachedInstance)
}))
Else
Await PublishResultAsync(context, request, uid, test.Name)
End If
Next
context.Complete()
End Function
一致するテストがない場合は、選択されていないテストを実行または発行せずにリクエストを完了します。 MTP は、「ゼロテスト」または「最小想定テスト」のポリシーを適用しています。
Important
TreeNodeFilterとCompositeTestExecutionFilterは試験段階であり、TPEXP診断 ID を使用します。 ツリー フィルターをサポートするフレームワークでは、テスト ツリーとプロパティに対してフィルターを評価する必要があります。
フィルター プロバイダーを使用するフレームワークでは、複合フィルターのすべての子も評価する必要があります。 この例のヘルパーは、 NopFilter と TestNodeUidListFilterのみをサポートしています。
正規テストの実行
MTP 2.5 プレビュー以降では、進行中の TestNodeUpdateMessage に対して ExecuteRequestContext.StartTestExecutionAsync を呼び出すことで、実験的な標準形式 TestExecution を開始できます。 start メソッドは、進行中の更新プログラムを発行し、実行ライフサイクル、メッセージの順序付け、テレメトリの相関関係を一緒に保持するオブジェクトを返します。
RunまたはRunAsyncを使用して、正規の実行コンテキストでフレームワークを呼び出し、コードをテストします。 OpenTelemetry がアクティブな場合、テストによって作成された子アクティビティは、同じテスト実行アクティビティの下に残ります。
順序付けられた結果メッセージとプロデューサーによって監視された実行の終了時刻を使用して CompleteAsync を呼び出します。 すべての結果は、開始メッセージのセッション、テスト UID、および親 UID を使用する必要があります。 最後のメッセージは最終的な結果です。 空のコレクションは、結果を発明することなく、実行が完了した更新を発行します。
結果を公開せずに中止するには、未完了の TestExecution を破棄します。 完了または破棄後、オブジェクトは追加の実行または完了呼び出しを拒否します。
Important
StartTestExecutionAsync と TPEXP は TestExecution 診断 ID を使用しており、今後のリリースで変更または削除される可能性があります。
TestNodeUpdateMessage データ
IMessageBus セクションで説明したように、メッセージ バスを使用する前に、提供するデータの型を指定する必要があります。 テストプラットフォームは、TestNodeUpdateMessage の概念を表現するために、既知の型である を定義しています。
ドキュメントのこのパートでは、このペイロード データを利用する方法について説明します。 表面を確認しましょう。
public sealed class TestNodeUpdateMessage(
SessionUid sessionUid,
TestNode testNode,
TestNodeUid? parentTestNodeUid = null)
{
public TestNode TestNode { get; }
public TestNodeUid? ParentTestNodeUid { get; }
}
public class TestNode
{
public required TestNodeUid Uid { get; init; }
public required string DisplayName { get; init; }
public PropertyBag Properties { get; init; } = new();
}
public sealed class TestNodeUid(string value);
public sealed partial class PropertyBag
{
public PropertyBag();
public PropertyBag(params IProperty[] properties);
public PropertyBag(IEnumerable<IProperty> properties);
public int Count { get; }
public void Add(IProperty property);
public bool Any<TProperty>();
public TProperty? SingleOrDefault<TProperty>();
public TProperty Single<TProperty>();
public TProperty[] OfType<TProperty>();
public IEnumerable<IProperty> AsEnumerable();
public IEnumerator<IProperty> GetEnumerator();
...
}
public interface IProperty
{
}
TestNodeUpdateMessage:TestNodeUpdateMessageはTestNodeとParentTestNodeUidの 2 つのプロパティで構成されます。ParentTestNodeUidはテストが親テストを持つ可能性を示し、テストツリーの概念を導入しています。TestNodeを互いに関連付けて配置することができます。 この構造により、ノード間のツリー関係に基づいて、将来の機能強化と機能が可能になります。 テスト フレームワークでテスト ツリー構造が必要ない場合は、それを使用せず、null に設定するだけで、TestNodeの単純なフラット リストを作成できます。TestNode:TestNodeは 3 つのプロパティで構成され、そのうちの 1 つは型UidのTestNodeUidです。 このUidは、ノードの一意の安定した ID として機能します。 "一意の安定した ID" という用語は、同じTestNodeが異なる実行間とシステム間で同一のUidを維持する必要があることを意味します。 このTestNodeUidは、テスト プラットフォームがそのまま受け入れる任意の不透明な文字列です。
Important
ID の安定性と一意性は、テスト ドメインでは重要です。 これらは、実行する単一テストの正確なターゲット設定を可能にし、ID がテストの永続的な識別子として機能できるようにして、強力な拡張機能や機能を支援します。
2 番目のプロパティは DisplayName で、テストのわかりやすい名前になります。 たとえば、--list-tests コマンド ラインを実行する際にこの名前が表示されます。
3 番目の属性は Properties で、PropertyBag 型になります。 コード内に示されているように、これは TestNodeUpdateMessage に関する一般的なプロパティを保持する特殊なプロパティ バッグです。 これは、プレースホルダー インターフェイス IProperty を実装するノードに任意のプロパティを追加できることを意味します。
テスト プラットフォームは、TestNode.Properties に追加された特定のプロパティを識別し、テストが合格したか、失敗したか、またはスキップされたかを判断します。
使用可能なプロパティの現在の一覧については、「TestNodeUpdateMessage.TestNode 」セクション相対説明を参照してください。
PropertyBag 型は、通常、すべての IData でアクセスでき、プラットフォームと拡張機能でクエリの実行ができるその他のプロパティを格納するために使用されます。 このメカニズムにより、破壊的変更を導入することなく、新しい情報でプラットフォームを強化できます。 コンポーネントはプロパティを認識すると、そのクエリを実行できます。認識しない場合は、無視します。
最後に、このセクションでは、テスト フレームワークの実装には、次のサンプル内のような IDataProducer を生成する TestNodeUpdateMessage を実装する必要があることについて説明します。
internal sealed class TestingFramework
: ITestFramework, IDataProducer
{
// ...
public Type[] DataTypesProduced =>
[
typeof(TestNodeUpdateMessage)
];
// ...
}
テスト アダプターが実行中に ファイル を生成する場合、プラットフォームは 2 つの異なるメカニズムを認識します。適切なメカニズムは、ファイルが特定のテストに属しているか、実行全体に属しているかによって異なります。 詳細については、「 メッセージ バス ファイルの成果物」を参照してください。
- 実行レベルまたはセッション レベルのファイルは、スタンドアロン のバス メッセージです。
FileArtifact(スコープなし) またはSessionFileArtifact(SessionUidを介して実行をスコープ指定) を発行します。 - テスト固有の添付ファイルは、スタンドアロン メッセージではありません。 それらを
FileArtifactProperty内のTestNode.PropertiesにTestNodeUpdateMessageエントリとして追加し、ターミナル、dotnet test、IDE が各ファイルをテストに関連付けます。
SessionFileArtifactを発行する場合は、次に示すように、DataTypesProducedを通じて事前にそのランタイム型をプラットフォームに宣言する必要があります。
FileArtifactPropertyは、独自のメッセージの種類ではなく、TestNodeUpdateMessage内を移動するため、個別に一覧表示されていないことに注意してください。
internal sealed class TestingFramework
: ITestFramework, IDataProducer
{
// ...
public Type[] DataTypesProduced =>
[
typeof(TestNodeUpdateMessage),
typeof(SessionFileArtifact)
];
// ...
}
既知のプロパティ
要求セクションで詳しく説明したように、テスト プラットフォームでは、TestNodeUpdateMessageの状態 (成功、失敗、スキップなど) を判断するために、TestNodeに追加された特定のプロパティが識別されます。 これにより、ランタイムは、失敗したテストの一覧と失敗したテストに対応する情報をコンソールで正確に表示したり、テスト プロセスに適切な終了コードを設定したりできるようになります。
要求セクションで詳しく説明したように、テスト プラットフォームでは、TestNodeUpdateMessageに追加された特定のプロパティを識別して、TestNodeの状態 (成功、失敗、スキップなど) を決定します。 これにより、ランタイムは、失敗したテストの一覧と失敗したテストに対応する情報をコンソールで正確に表示したり、テスト プロセスに適切な終了コードを設定したりできるようになります。
このセクションでは、よく知られているさまざまな IProperty オプションと、それぞれの影響について説明します。
既知のプロパティの包括的な一覧については、 TestNodeStateProperties.cs (および同じディレクトリ内の他の Property 型) を参照してください。 プロパティの説明が見つからない場合は、問題を提出してください。
これらのプロパティは、次のカテゴリに分類できます。
- 一般的な情報: 任意の種類の要求に含めることができるプロパティ。
-
検出情報:
DiscoverTestExecutionRequest検出要求中に指定されるプロパティ。 -
実行情報: テスト実行要求
RunTestExecutionRequest中に指定されるプロパティ。
特定のプロパティは必須ですが、その他のプロパティは省略可能です。 必須プロパティは、失敗したテストを報告したり、テスト セッション全体が成功したかどうかを示したりするなど、基本的なテスト機能を提供するために必要です。
一方、省略可能なプロパティは、追加情報を提供することでテスト エクスペリエンスを強化します。 これらは特に、IDE シナリオ (VS、VSCode など) や、コンソールの実行、または正しく機能するためにより詳細な情報を必要とする特定の拡張機能をサポートする際に有用です。 ただし、これらの省略可能なプロパティは、テストの実行には影響しません。
注
拡張機能は、正しく動作するために特定の情報が必要な場合、例外を通知して管理する役割を担います。 拡張機能は、必要な情報が欠落している場合、テスト実行の失敗を引き起こすのではなく、単にオプトアウトします。
一般的な情報
public record struct LinePosition(
int Line,
int Column);
public record struct LinePositionSpan(
LinePosition Start,
LinePosition End);
public abstract record FileLocationProperty(
string FilePath,
LinePositionSpan LineSpan)
: IProperty;
public sealed record TestFileLocationProperty(
string FilePath,
LinePositionSpan LineSpan)
: FileLocationProperty(FilePath, LineSpan);
TestFileLocationProperty は、ソース ファイル内のテストの場所を特定するために使用されます。 これは、イニシエーターが Visual Studio や Visual Studio Code などの IDE である場合に特に便利です。
public sealed record TestMethodIdentifierProperty(
string AssemblyFullName,
string Namespace,
string TypeName,
string MethodName,
string[] ParameterTypeFullNames,
string ReturnTypeFullName)
TestMethodIdentifierProperty は、テスト メソッドの一意識別子です。
ポータブル キー値メタデータを発行するには、Microsoft.Testing.Platform.Extensions.MessagesからTestMetadataPropertyを作成し、TestNode.Propertiesに追加します。
var metadata = new TestMetadataProperty("Environment", "Staging");
Dim metadata = New TestMetadataProperty("Environment", "Staging")
MTP では、JSON 検出出力とサーバー モードの更新でこれらのプロパティが特性として公開されます。 任意の IProperty 実装は、インプロセス コンシューマーが引き続き使用できますが、外部クライアントに自動的にシリアル化されることはありません。 プラットフォームの内部 SerializableKeyValuePairStringProperty 型は、公開された拡張ポイントではありません。
MTP 2.4 以降では、失敗したテスト ノードに AssertionFailureProperty を追加して、事前フォーマットされた個別の予期された値と実際の値を指定します。 レポート コンシューマーは、エラー メッセージを解析する代わりに、これらの値を使用して構造化された違いをレンダリングできます。 少なくとも 1 つの値がnull以外である必要があります。 コンシューマーはこのプロパティを優先しますが、Dataの従来のassert.expectedとassert.actualエントリにフォールバックできます。
MTP 2.4 以降では、インプロセス再試行シーケンス内のすべての更新に RetryAttemptProperty を追加します。
AttemptNumber は 1 始まりで、IsSuperseded は後の試行によって置き換えられる試行を示します。 TRX、JUnit、およびプロセス終了コードは置き換えられた試行を無視しますが、ターミナル、HTML、CTRF コンシューマーは完全な履歴を保持できます。 このプロパティは、プロセス外の --retry-failed-tests 試行を表していません。
キャプチャされたテスト出力
キャプチャしたテキストをテストに関連付けるには、StandardOutputPropertyからMicrosoft.Testing.Platform.Extensions.MessagesとTestNode.PropertiesをそのテストのStandardErrorPropertyに追加します。 テスト結果と同じ安定した UID とセッションを使用して、 TestNodeUpdateMessage を介してノードを発行します。
このフレームワークは、並列テスト間の分離を含め、出力の取得と帰属を管理します。 MTP はプロパティを取り込みます。WriteLine への任意の呼び出しは自動的にキャプチャされません。 次の例では、実際のフレームワークによってキャプチャされた出力の代わりにサンプル テキストを使用します。
var testNode = new TestNode
{
Uid = uid,
DisplayName = name,
Properties = new PropertyBag(
PassedTestNodeStateProperty.CachedInstance,
new StandardOutputProperty($"Output from {name}."),
new StandardErrorProperty($"Diagnostic from {name}."))
};
await context.MessageBus.PublishAsync(
this, new TestNodeUpdateMessage(request.Session.SessionUid, testNode));
Dim testNode As New TestNode With {
.Uid = uid,
.DisplayName = name,
.Properties = New PropertyBag(
PassedTestNodeStateProperty.CachedInstance,
New StandardOutputProperty($"Output from {name}."),
New StandardErrorProperty($"Diagnostic from {name}."))
}
Await context.MessageBus.PublishAsync(
Me, New TestNodeUpdateMessage(request.Session.SessionUid, testNode))
ターミナル レポーターは、 --show-stdout と --show-stderrでこれらのプロパティを使用します。 標準エラー プロパティでは、テストは失敗しません。テストの状態プロパティによって結果が決まります。
IOutputDevice を通じて、各テストごとの出力を、診断ログや拡張機能レベルの出力とは区別するようにしてください。 TRX レポートにテキストを含めるには、TRX 固有のメッセージ プロパティも追加で指定してください。 すべてのレポート コンシューマーが同じプロパティをマップするとは考えないでください。
検出情報
public sealed record DiscoveredTestNodeStateProperty(
string? Explanation = null)
{
public static DiscoveredTestNodeStateProperty CachedInstance { get; }
}
DiscoveredTestNodeStateProperty は、TestNode が検出されたことを示します。 これは、DiscoverTestExecutionRequest がテスト フレームワークに送信される際に使用されます。
CachedInstance プロパティで提供される便利なキャッシュ値を書き留めます。
このプロパティは必須です。
実行情報
public sealed record InProgressTestNodeStateProperty(
string? Explanation = null)
{
public static InProgressTestNodeStateProperty CachedInstance { get; }
}
InProgressTestNodeStateProperty は、TestNode の実行がスケジュールされており、現在進行中であることをテスト プラットフォームに通知します。
CachedInstance プロパティで提供される便利なキャッシュ値を書き留めます。
public readonly record struct TimingInfo(
DateTimeOffset StartTime,
DateTimeOffset EndTime,
TimeSpan Duration);
public sealed record StepTimingInfo(
string Id,
string Description,
TimingInfo Timing);
public sealed record TimingProperty : IProperty
{
public TimingProperty(TimingInfo globalTiming)
: this(globalTiming, [])
{
}
public TimingProperty(
TimingInfo globalTiming,
StepTimingInfo[] stepTimings)
{
GlobalTiming = globalTiming;
StepTimings = stepTimings;
}
public TimingInfo GlobalTiming { get; }
public StepTimingInfo[] StepTimings { get; }
}
TimingProperty は、TestNode の実行に関するタイミングの詳細をリレーするために使用されます。 また、StepTimingInfo を介して個々の実行ステップのタイミングを可能にします. これは特に、初期化、実行、クリーンアップなど、テストの概念が複数のフェーズに分かれている場合に有用です。
次のプロパティのうち 1 つだけ が ごとにTestNodeであり、TestNode の結果をテスト プラットフォームに伝えます。
public sealed record PassedTestNodeStateProperty(
string? Explanation = null)
: TestNodeStateProperty(Explanation)
{
public static PassedTestNodeStateProperty CachedInstance
{ get; } = new PassedTestNodeStateProperty();
}
PassedTestNodeStateProperty は、この TestNode が渡されたことをテスト プラットフォームに通知します。
CachedInstance プロパティで提供される便利なキャッシュ値を書き留めます。
public sealed record SkippedTestNodeStateProperty(
string? Explanation = null)
: TestNodeStateProperty(Explanation)
{
public static SkippedTestNodeStateProperty CachedInstance
{ get; } = new SkippedTestNodeStateProperty();
}
SkippedTestNodeStateProperty は、この TestNode がスキップされたことをテスト プラットフォームに通知します。
CachedInstance プロパティで提供される便利なキャッシュ値を書き留めます。
public sealed record FailedTestNodeStateProperty : TestNodeStateProperty
{
public FailedTestNodeStateProperty()
: base(default(string))
{
}
public FailedTestNodeStateProperty(string explanation)
: base(explanation)
{
}
public FailedTestNodeStateProperty(
Exception exception,
string? explanation = null)
: base(explanation ?? exception.Message)
{
Exception = exception;
}
public Exception? Exception { get; }
}
FailedTestNodeStateProperty は、この TestNode がアサーションの後に失敗したことをテスト プラットフォームに通知します。
public sealed record ErrorTestNodeStateProperty : TestNodeStateProperty
{
public ErrorTestNodeStateProperty()
: base(default(string))
{
}
public ErrorTestNodeStateProperty(string explanation)
: base(explanation)
{
}
public ErrorTestNodeStateProperty(
Exception exception,
string? explanation = null)
: base(explanation ?? exception.Message)
{
Exception = exception;
}
public Exception? Exception { get; }
}
ErrorTestNodeStateProperty は、この TestNode が失敗したことをテスト プラットフォームに通知します。 この種類のエラーは、アサーション エラーに使用される FailedTestNodeStateProperty とは異なります。 たとえば、テスト初期化エラーなどの問題を ErrorTestNodeStateProperty を使用して報告できます。
public sealed record TimeoutTestNodeStateProperty : TestNodeStateProperty
{
public TimeoutTestNodeStateProperty()
: base(default(string))
{
}
public TimeoutTestNodeStateProperty(string explanation)
: base(explanation)
{
}
public TimeoutTestNodeStateProperty(
Exception exception,
string? explanation = null)
: base(explanation ?? exception.Message)
{
Exception = exception;
}
public Exception? Exception { get; }
public TimeSpan? Timeout { get; init; }
}
TimeoutTestNodeStateProperty は、この TestNode がタイムアウトの理由で失敗したことをテスト プラットフォームに通知します。
Timeout プロパティを使用して、タイムアウトを報告できます。
public sealed record CancelledTestNodeStateProperty : TestNodeStateProperty
{
public CancelledTestNodeStateProperty()
: base(default(string))
{
}
public CancelledTestNodeStateProperty(string explanation)
: base(explanation)
{
}
public CancelledTestNodeStateProperty(
Exception exception,
string? explanation = null)
: base(explanation ?? exception.Message)
{
Exception = exception;
}
public Exception? Exception { get; }
}
CancelledTestNodeStateProperty は、この TestNode がキャンセルにより失敗したことをテスト プラットフォームに通知します。
.NET