Microsoft.Testing.Platform は、テスト フレームワークと、インプロセスまたはアウトプロセスで動作できる任意の数の拡張機能で構成されています。
アーキテクチャ セクションで説明されているように、Microsoft.Testing.Platform は、さまざまなシナリオと拡張性ポイントに対応するように設計されています。 主な必須の拡張機能は、テストで使用されるテスト フレームワークであることは間違いありません。 この登録に失敗すると、スタートアップ エラーが発生します。 テスト フレームワークは、テスト セッションを実行するために必要な唯一の必須の拡張機能です。
テスト レポートの生成、コード カバレッジ、失敗したテストの再試行、その他の可能な機能といったシナリオをサポートするには、テスト フレームワーク自体では本質的に提供されないこれらの機能を実現するために他の拡張機能がテスト フレームワークと連動できるようなメカニズムを用意する必要があります。
本質的に、テスト フレームワークは、テスト スイートを構成する各テストの情報を提供する主な拡張機能です。 特定のテストが成功したか、失敗したか、またはスキップされたかを報告します。また、特に人間が読み取れる名前 (表示名と呼ばれる)、ソース ファイル、テストの開始行といった各テストの追加情報を提供できます。
拡張可能性ポイントにより、テスト フレームワークで提供される情報を利用して新しい成果物を生成したり、既存の成果物に追加機能を付加して強化することができます。 一般的に使用される拡張機能は TRX レポート ジェネレーターで、TestNodeUpdateMessage を登録してそこから XML レポート ファイルを生成します。
アーキテクチャで説明したように、テスト フレームワークと同じプロセス内で操作できない特定の拡張点があります。 通常、その理由は次のとおりです。
- テスト ホストの環境変数を変更する必要がある。 テスト ホスト プロセス自体での動作が遅すぎる。
- テストとユーザー コードが実行されるテスト ホストには、プロセス自体を不安定にするユーザー コード バグが含まれている可能性があり、それがハングやクラッシュにつながる可能性もあるため、外部からプロセスを監視する必要があります。 このような場合、拡張機能は、テスト ホスト プロセスと連動してクラッシュまたはハングします。
これらの理由により、拡張点は次の 2 種類に分類されます。
インプロセス拡張機能: これらの拡張機能は、テスト フレームワークと同じプロセス内で動作します。
インプロセス拡張機能は、次のように
ITestApplicationBuilder.TestHostプロパティを使用して登録できます。// ... var builder = await TestApplication.CreateBuilderAsync(args); builder.TestHost.AddXXX(...); // ...アウトプロセス拡張機能: これらの拡張機能は別のプロセスで機能するため、テスト ホスト自体の影響を受けずにテスト ホストを監視できます。
アウトプロセス拡張機能は
ITestApplicationBuilder.TestHostControllersを使用して登録できます。var builder = await TestApplication.CreateBuilderAsync(args); builder.TestHostControllers.AddXXX(...);最後に、一部の拡張機能は両方のシナリオで機能するように設計されています。 これらの一般的な拡張機能は、両方のホストで同じように動作します。 これらの拡張機能は、TestHost インターフェイスと TestHostController インターフェースを介して、または
ITestApplicationBuilderレベルで直接登録できます。 このような拡張機能の例には、ICommandLineOptionsProvider があります。
IExtension インターフェイス
IExtension インターフェイスはテスト プラットフォーム内のすべての拡張ポイントの基本インターフェイスとして機能します。 これは主に拡張機能に関する説明情報を取得するために、そして最も重要なこととして拡張機能自体を有効または無効にするために使用されます。
次の IExtension インターフェイスについて考えてみましょう。
public interface IExtension
{
string Uid { get; }
string Version { get; }
string DisplayName { get; }
string Description { get; }
Task<bool> IsEnabledAsync();
}
Uid: 拡張機能の一意の識別子を表します。 他の拡張機能との競合を避けるため、この文字列には一意の値を選択することが重要です。Version: インターフェイスのバージョンを表します。 セマンティック バージョン管理が必要です。DisplayName:--infoコマンド ライン オプションを使用して情報を要求する際にログに表示される、わかりやすい名前表現。Description: 拡張機能の説明で、--infoコマンド ライン オプションを使用して情報を要求する際に表示されます。IsEnabledAsync(): このメソッドは、拡張機能がインスタンス化される際にテスト プラットフォームによって呼び出されます。 メソッドがfalseを返すと、拡張機能は除外されます。 このメソッドは通常、構成ファイルまたは一部のカスタム コマンド ライン オプションに基づいて決定を行います。 多くの場合、ユーザーはコマンド ラインで--customExtensionOptionを指定して拡張機能自体をオプトインします。
テスト フレームワーク拡張機能
テスト フレームワークは、テスト プラットフォームにテストを検出して実行する機能を提供する主要な拡張機能です。 テスト フレームワークは、テストの結果をテスト プラットフォームに戻す役割を担います。 テスト フレームワークは、テスト セッションを実行するために必要な唯一の必須の拡張機能です。
テスト フレームワークを登録する
このセクションでは、テスト フレームワークをテスト プラットフォームに登録する方法について説明します。
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>: これは、 によって返されるインスタンスである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 を返しても、テスト セッションの実行が妨げられることはありません。 すべてのテスト フレームワークは、テストを検出して実行できる必要があります。 その影響は、テスト フレームワークに特定の機能がない場合にオプトアウトする可能性がある拡張機能に限定する必要があります。
テスト フレームワークを作成する
Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework は、テスト フレームワークを提供する拡張機能によって実装されます。
public interface ITestFramework : IExtension
{
Task<CreateTestSessionResult> CreateTestSessionAsync(CreateTestSessionContext context);
Task ExecuteRequestAsync(ExecuteRequestContext context);
Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context);
}
ITestFramework インターフェイスは、IExtension インターフェイス (すべての拡張点が継承するインターフェイス) を継承します。
IExtension は、拡張機能の名前と説明を取得するために使用されます。 また IExtension は、Task<bool> IsEnabledAsync() を通じて、セットアップで拡張機能を動的に有効または無効にする方法も提供します。 特別なニーズがない場合は、このメソッドから true を返すようにしてください。
CreateTestSessionAsync メソッド
CreateTestSessionAsync メソッドはテスト セッションの開始時に呼び出され、テスト フレームワークの初期化に使用されます。 API は CloseTestSessionContext オブジェクトを受け入れ、CloseTestSessionResult を返します。
public sealed class CreateTestSessionContext : TestSessionContext
{
public SessionUid SessionUid { get; }
public ClientInfo Client { get; }
public CancellationToken CancellationToken { get; }
}
public readonly struct SessionUid
{
public string Value { get; }
}
public sealed class ClientInfo
{
public string Id { get; }
public string Version { get; }
}
SessionUid は、現在のテスト セッションの一意識別子として機能し、セッションの結果に論理接続します。
ClientInfo は、テスト フレームワークを呼び出すエンティティの詳細を提供します。 この情報は、テスト フレームワークが動作を変更するために利用される場合があります。 たとえば、このドキュメントが作成された時点では、コンソールの実行で "testingplatform-console" などのクライアント名が報告されるでしょう。
CancellationToken を使用して、CreateTestSessionAsync の実行を停止します。
戻りオブジェクトは、次のように CloseTestSessionResult になります。
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: このトークンは、特定の要求の処理を中断するために使用されます。
Complete(): 前のシーケンスで示したように、Complete メソッドは、要求が正常に処理され、関連するすべての情報が IMessageBus に送信されたことをプラットフォームに通知します。
Warnung
要求で Complete() の呼び出しを無視すると、テスト アプリケーションが応答しなくなります。
テスト フレームワークを自分自身の要件またはユーザーの要件に合わせてカスタマイズするには、構成ファイル内のパーソナライズされたセクションを使用するか、カスタム コマンド ライン オプションを使用します。
要求の処理
次のセクションでは、テスト フレームワークが受信して処理する可能性のあるさまざまな要求について詳しく説明します。
次のセクションに進む前に、テスト実行情報をテスト プラットフォームに伝えるための重要なサービス、IMessageBus の概念について十分理解することが重要です。
TestSessionContext
TestSessionContext はすべての要求の共有プロパティで、進行中のテスト セッションに関する情報を提供します。
public class TestSessionContext
{
public SessionUid SessionUid { get; }
public ClientInfo Client { get; }
}
public readonly struct SessionUid(string value)
{
public string Value { get; }
}
public sealed class ClientInfo
{
public string Id { get; }
public string Version { get; }
}
TestSessionContext は、進行中のテスト セッションの一意識別子である SessionUid で構成され、テスト セッション データのログ作成と関連付けを支援します。 また、テスト セッションのClientInfoに関する詳細を提供する 型も含まれます。 テスト フレームワークは、テスト セッションのイニシエーターの ID に基づいて、異なるルートを選択したり、さまざまな情報を公開したりすることがあります。
DiscoverTestExecutionRequest(テスト実行リクエストの検出)
public class DiscoverTestExecutionRequest
{
// Detailed in the custom section below
public TestSessionContext Session { get; }
// This is experimental and intended for future use, please disregard for now.
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
{
// Detailed in the custom section below
public TestSessionContext Session { get; }
// This is experimental and intended for future use, please disregard for now.
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));
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は、テスト プラットフォームがそのまま受け入れる任意の不透明な文字列です。
Von Bedeutung
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)
];
// ...
}
実行中にテスト アダプターがファイルの公開を必要とする場合は、認識されるプロパティは次のソース ファイルで確認できます。https://github.com/microsoft/testfx/blob/main/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs ご覧のように、ファイル資産を一般的な方法で提供したり、特定の TestNode と関連付けたりすることができます。
SessionFileArtifact をプッシュする場合は、以下に示すように、事前にプラットフォームに宣言する必要があります。
internal sealed class TestingFramework
: ITestFramework, IDataProducer
{
// ...
public Type[] DataTypesProduced =>
[
typeof(TestNodeUpdateMessage),
typeof(SessionFileArtifact)
];
// ...
}
既知のプロパティ
要求セクションで詳しく説明したように、テスト プラットフォームでは、TestNodeUpdateMessageの状態 (成功、失敗、スキップなど) を判断するために、TestNodeに追加された特定のプロパティが識別されます。 これにより、ランタイムは、失敗したテストの一覧と失敗したテストに対応する情報をコンソールで正確に表示したり、テスト プロセスに適切な終了コードを設定したりできるようになります。
このセグメントでは、さまざまな既知の IProperty オプションとそれぞれの影響について説明します。
既知のプロパティの包括的な一覧については、 TestNodeProperties.csを参照してください。 プロパティの説明が見つからない場合は、問題を提出してください。
これらのプロパティは、次のカテゴリに分類できます。
- 一般的な情報: 任意の種類の要求に含めることができるプロパティ。
-
検出情報:
DiscoverTestExecutionRequest検出要求中に指定されるプロパティ。 -
実行情報: テスト実行要求
RunTestExecutionRequest中に指定されるプロパティ。
特定のプロパティは必須ですが、その他のプロパティは省略可能です。 必須プロパティは、失敗したテストを報告したり、テスト セッション全体が成功したかどうかを示したりするなど、基本的なテスト機能を提供するために必要です。
一方、省略可能なプロパティは、追加情報を提供することでテスト エクスペリエンスを強化します。 これらは特に、IDE シナリオ (VS、VSCode など) や、コンソールの実行、または正しく機能するためにより詳細な情報を必要とする特定の拡張機能をサポートする際に有用です。 ただし、これらの省略可能なプロパティは、テストの実行には影響しません。
注
拡張機能は、正しく動作するために特定の情報が必要な場合、例外を通知して管理する役割を担います。 拡張機能は、必要な情報が欠落している場合、テスト実行の失敗を引き起こすのではなく、単にオプトアウトします。
一般的な情報
public record KeyValuePairStringProperty(
string Key,
string Value)
: IProperty;
KeyValuePairStringProperty は、一般的なキーと値のペア データを表します。
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 は、ECMA-335 標準に準拠するテスト メソッドの一意識別子です。
注
このプロパティを作成するために必要なデータは、System.Reflection 名前空間の型を使用する、.NETのリフレクション機能を使用して簡単に取得できます。
public sealed record TestMetadataProperty(
string Key,
string Value)
TestMetadataProperty は の特性またはTestNodeを伝えるために使用されます。
検出情報
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 がキャンセルにより失敗したことをテスト プラットフォームに通知します。
その他の拡張ポイント
テスト プラットフォームには、プラットフォームとテスト フレームワークの動作をカスタマイズできる追加の拡張ポイントが用意されています。 これらの拡張ポイントは省略可能であり、テスト エクスペリエンスを強化するために使用できます。
ICommandLineOptionsProvider 拡張拡張
注
この API を拡張すると、カスタム拡張機能はテスト ホスト プロセスの内外の両方に存在することになります。
アーキテクチャ セクションで説明したように、最初の手順では、ITestApplicationBuilder を作成し、それを使用してテスト フレームワークと拡張機能を登録する必要があります。
var builder = await TestApplication.CreateBuilderAsync(args);
CreateBuilderAsync メソッドは、string[] という名前の文字列 (args) の配列を受け入れます。 これらの引数は、テスト プラットフォームのすべてのコンポーネント (組み込みコンポーネント、テスト フレームワーク、および拡張機能を含む) にコマンド ライン オプションを渡すために使用でき、それらの動作をカスタマイズすることが可能です。
通常、渡される引数は標準 Main(string[] args) メソッドで受け取った引数です。 ただし、ホスティング環境が異なる場合は、引数の一覧を指定できます。
引数には二重ダッシュ を--。 たとえば、--filter のようにします。
テスト フレームワークや拡張点などのコンポーネントでカスタム コマンド ライン オプションを提供する必要がある場合は、ICommandLineOptionsProvider インターフェイスを実装することでこれを行うことができます。 その後、この実装は、次に示すように ITestApplicationBuilder プロパティの登録ファクトリを介して CommandLine に登録できます。
builder.CommandLine.AddProvider(
static () => new CustomCommandLineOptions());
提供されている例では、CustomCommandLineOptions は ICommandLineOptionsProvider インターフェイスの実装です。このインターフェイスは、次のメンバーとデータ型で構成されます。
public interface ICommandLineOptionsProvider : IExtension
{
IReadOnlyCollection<CommandLineOption> GetCommandLineOptions();
Task<ValidationResult> ValidateOptionArgumentsAsync(
CommandLineOption commandOption,
string[] arguments);
Task<ValidationResult> ValidateCommandLineOptionsAsync(
ICommandLineOptions commandLineOptions);
}
public sealed class CommandLineOption
{
public string Name { get; }
public string Description { get; }
public ArgumentArity Arity { get; }
public bool IsHidden { get; }
// ...
}
public interface ICommandLineOptions
{
bool IsOptionSet(string optionName);
bool TryGetOptionArgumentList(
string optionName,
out string[]? arguments);
}
以上のように、ICommandLineOptionsProviderは IExtension インターフェイスを拡張します。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
ICommandLineOptionsProvider の実行順序は次のとおりです。
API とその平均値を調べてみましょう。
ICommandLineOptionsProvider.GetCommandLineOptions(): このメソッドは、コンポーネントで提供されるすべてのオプションを取得するために使用されます。 各 CommandLineOption は、次のプロパティを指定する必要があります。
string name: これはオプションの名前で、ダッシュなしで表示されます。 たとえば、filter はユーザーにより --filter として使用されます。
string description: これはオプションの説明です。 ユーザーがアプリケーション ビルダーに --help を引数として渡すと表示されます。
ArgumentArity arity: オプションのアリティとは、そのオプションまたはコマンドが指定された場合に渡すことができる値の数を指します。 現在使用可能な機能は次のとおりです。
-
Zero: 引数アリティが 0 であることを表します。 -
ZeroOrOne: 引数アリティが 0 または 1 であることを表します。 -
ZeroOrMore: 引数アリティが 0 以上であることを表します。 -
OneOrMore: 引数アリティが 1 つ以上であることを表します。 -
ExactlyOne: 引数アリティが 1 つのみであることを表します。
例については、System.CommandLine アリティ テーブルを参照してください。
bool isHidden: このプロパティは、オプションは使用できるものの、--help が呼び出されると説明には表示されないことを示します。
ICommandLineOptionsProvider.ValidateOptionArgumentsAsync: このメソッドは、ユーザーによって提供される引数を検証するために使用されます。
たとえば、カスタム テスト フレームワークの並列処理の程度を表す、--dop という名前のパラメーターがある場合、ユーザーは --dop 0 を入力する可能性があります。 このシナリオでは、並列処理の程度は 0 以上が想定されるため、値 1 は無効になります。
ValidateOptionArgumentsAsync を使用すると、事前検証を実行し、必要に応じてエラー メッセージを返すことができます。
上記のサンプルに対して可能な実装は、次のとおりです。
public Task<ValidationResult> ValidateOptionArgumentsAsync(
CommandLineOption commandOption,
string[] arguments)
{
if (commandOption.Name == "dop")
{
if (!int.TryParse(arguments[0], out int dopValue) || dopValue <= 0)
{
return ValidationResult.InvalidTask("--dop must be a positive integer");
}
}
return ValidationResult.ValidTask;
}
ICommandLineOptionsProvider.ValidateCommandLineOptionsAsync: このメソッドは最後のメソッドとして呼び出され、グローバルな一貫性チェックを実行できます。
たとえば、テスト フレームワークに、テスト結果レポートを生成してファイルに保存する機能があるとします。 この機能には --generatereport オプションを使用してアクセスでき、ファイル名は --reportfilename myfile.rep で指定されます。 このシナリオでは、ユーザーがファイル名を指定せずに --generatereport オプションのみを指定した場合、ファイル名なしでレポートを生成できないため、検証は失敗します。
上記のサンプルに対して可能な実装は、次のとおりです。
public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
{
bool generateReportEnabled = commandLineOptions.IsOptionSet(GenerateReportOption);
bool reportFileName = commandLineOptions.TryGetOptionArgumentList(ReportFilenameOption, out string[]? _);
return (generateReportEnabled || reportFileName) && !(generateReportEnabled && reportFileName)
? ValidationResult.InvalidTask("Both `--generatereport` and `--reportfilename` need to be provided simultaneously.")
: ValidationResult.ValidTask;
}
ValidateCommandLineOptionsAsync メソッドは、プラットフォーム自体によって解析された引数情報をフェッチするために使用される ICommandLineOptions サービスを提供することに注意してください。
ITestSessionLifetimeHandler 拡張拡張
ITestSessionLifeTimeHandler は、テスト セッションの前後にコードを実行できるインプロセス拡張機能です。
カスタム ITestSessionLifeTimeHandler を登録するには、次の API を利用します。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestSessionLifetimeHandle(
static serviceProvider => new CustomTestSessionLifeTimeHandler());
ファクトリは IServiceProvider を利用して、テスト プラットフォームで提供されるサービス スイートへのアクセスを取得します。
Von Bedeutung
API は登録された順序で呼び出されるため、登録のシーケンスは重要です。
ITestSessionLifeTimeHandler インターフェイスには、以下のメソッドが含まれます。
public interface ITestSessionLifetimeHandler : ITestHostExtension
{
Task OnTestSessionStartingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
Task OnTestSessionFinishingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
}
public readonly struct SessionUid(string value)
{
public string Value { get; } = value;
}
public interface ITestHostExtension : IExtension
{
}
ITestSessionLifetimeHandler は、すべてのITestHostExtension拡張機能のベースとして機能する の型です。 他のすべての拡張点と同様に、IExtension からも継承されます。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
この API の詳細に注意を払いましょう。
OnTestSessionStartingAsync: このメソッドは、テスト セッションの開始前に呼び出され、現在のテスト セッションに不透明な識別子を提供する SessionUid オブジェクトを受け取ります。
OnTestSessionFinishingAsync: このメソッドは、テスト セッションの完了後に呼び出され、テスト フレームワークがすべてのテストの実行を完了しており、関連するすべてのデータをプラットフォームに報告していることを確認します。 通常、このメソッドでは、拡張機能はカスタム資産またはデータを共有プラットフォーム バスに送信するために IMessageBus を使用します。 このメソッドは、テスト セッションが終了したことをカスタムのアウトプロセス拡張機能に通知することもできます。
最後に、どちらの API も、拡張機能が受け入れる必要がある CancellationToken を取得します。
拡張機能が集中的な初期化を必要としており、ユーザーが async/await パターンを使用する必要がある場合には、Async extension initialization and cleanup を参照できます。 拡張点の間で状態を共有する必要がある場合は、CompositeExtensionFactory<T> セクションを参照できます。
ITestApplicationLifecycleCallbacks 拡張拡張
ITestApplicationLifecycleCallbacks は、何よりも先にコードを実行できるようにするインプロセス拡張機能であり、テスト ホストの架空メインの最初の行にアクセスできるようなものです。
カスタム ITestApplicationLifecycleCallbacks を登録するには、次の API を利用します。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestApplicationLifecycleCallbacks(
static serviceProvider
=> new CustomTestApplicationLifecycleCallbacks());
ファクトリは IServiceProvider を利用して、テスト プラットフォームで提供されるサービス スイートへのアクセスを取得します。
Von Bedeutung
API は登録された順序で呼び出されるため、登録のシーケンスは重要です。
ITestApplicationLifecycleCallbacks インターフェイスには、以下のメソッドが含まれます。
public interface ITestApplicationLifecycleCallbacks : ITestHostExtension
{
Task BeforeRunAsync(CancellationToken cancellationToken);
Task AfterRunAsync(
int exitCode,
CancellationToken cancellation);
}
public interface ITestHostExtension : IExtension
{
}
ITestApplicationLifecycleCallbacks は、すべてのITestHostExtension拡張機能のベースとして機能する の型です。 他のすべての拡張点と同様に、IExtension からも継承されます。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
BeforeRunAsync: このメソッドは、テスト ホストの最初の接触点として機能し、インプロセス拡張機能が機能を実行する最初の機会です。 通常、機能が両方の環境で動作するように設計されている場合、対応するアウトプロセス拡張機能との接続を確立するために使用されます。
例えば、組み込みのハング ダンプ機能は、インプロセスとアウトプロセスの両方の拡張機能で構成されており、このメソッドは拡張機能のアウトプロセス コンポーネントと情報を交換するために使用されます。
AfterRunAsync: このメソッドは int ITestApplication.RunAsync() を終了する前の最後の呼び出しであり、exit code を提供します。 これは、もっぱらクリーンアップ タスクのためと、対応するアウトプロセス拡張機能にテスト ホストが終了しようとしていることを通知するために使用する必要があります。
最後に、どちらの API も、拡張機能が受け入れる必要がある CancellationToken を取得します。
IDataConsumer 拡張拡張
IDataConsumer は、テスト フレームワークおよびその拡張機能によって IData にプッシュされる 情報をサブスクライブおよび受信することができるインプロセス拡張機能です。
この拡張点は、それによって開発者がテスト セッション中に生成されたすべての情報を収集して処理できるため、非常に重要です。
カスタム IDataConsumer を登録するには、次の API を利用します。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddDataConsumer(
static serviceProvider => new CustomDataConsumer());
ファクトリは IServiceProvider を利用して、テスト プラットフォームで提供されるサービス スイートへのアクセスを取得します。
Von Bedeutung
API は登録された順序で呼び出されるため、登録のシーケンスは重要です。
IDataConsumer インターフェイスには、以下のメソッドが含まれます。
public interface IDataConsumer : ITestHostExtension
{
Type[] DataTypesConsumed { get; }
Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken);
}
public interface IData
{
string DisplayName { get; }
string? Description { get; }
}
IDataConsumer は、すべてのITestHostExtension拡張機能のベースとして機能する の型です。 他のすべての拡張点と同様に、IExtension からも継承されます。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
DataTypesConsumed: このプロパティは、この拡張機能が使用する予定の Type の一覧を返します。 これは IDataProducer.DataTypesProduced に対応します。 特に、IDataConsumer は異なる IDataProducer インスタンスから発生した複数の型を問題なくサブスクライブできます。
ConsumeAsync: このメソッドは、現在のコンシューマーがサブスクライブされている型のデータが IMessageBus にプッシュされるたびにトリガーされます。
IDataProducer を受け取って、データ ペイロードのプロデューサーと IData ペイロード自体に関する詳細を提供します。 ご覧のように、IData は、一般的な情報データを含む汎用プレースホルダー インターフェイスです。 さまざまな種類の IData をプッシュする能力は、コンシューマーが種類自体を切り替えることで、それを正しい種類にキャストし、特定の情報にアクセスする必要があることを意味します。
TestNodeUpdateMessageによって生成された を詳しく説明するコンシューマの実装の例は、次のようになります。
internal class CustomDataConsumer : IDataConsumer, IOutputDeviceDataProducer
{
public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) };
...
public Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken)
{
var testNodeUpdateMessage = (TestNodeUpdateMessage)value;
switch (testNodeUpdateMessage.TestNode.Properties.Single<TestNodeStateProperty>())
{
case InProgressTestNodeStateProperty _:
{
...
break;
}
case PassedTestNodeStateProperty _:
{
...
break;
}
case FailedTestNodeStateProperty failedTestNodeStateProperty:
{
...
break;
}
case SkippedTestNodeStateProperty _:
{
...
break;
}
...
}
return Task.CompletedTask;
}
...
}
最後に、API は拡張機能が受け入れると想定されている CancellationToken を取得します。
Von Bedeutung
ConsumeAsync メソッド内でペイロードを直接処理することが重要です。
IMessageBus は、同期処理と非同期処理の両方を管理し、テスト フレームワークを使用して実行を調整できます。 消費プロセスは完全に非同期で、書き込み時に IMessageBus.Push をブロックすることはありませんが、これは将来の要件により今後変更される可能性のある実装の詳細です。 ただし、このプラットフォームでは、このメソッドが常に 1 回呼び出されるため、複雑な同期が不要になります。また、コンシューマーのスケーラビリティも管理されます。
Warnung
IDataConsumerで ITestHostProcessLifetimeHandler と組み合わせて を使用する場合は、ITestSessionLifetimeHandler.OnTestSessionFinishingAsync の実行後に受信したデータを無視することが重要です。
OnTestSessionFinishingAsync は、蓄積されたデータを処理し、新しい情報を IMessageBus に送信する最後の機会であるため、この時点以降に使用されるデータは拡張機能では利用できません。
拡張機能が集中的な初期化を必要としており、ユーザーが async/await パターンを使用する必要がある場合には、Async extension initialization and cleanup を参照できます。 拡張点の間で状態を共有する必要がある場合は、CompositeExtensionFactory<T> セクションを参照できます。
ITestHostEnvironmentVariableProvider 拡張拡張
ITestHostEnvironmentVariableProvider は、テスト ホストのカスタム環境変数を確立できるアウトプロセス拡張機能です。 この拡張点を使用すると、アーキテクチャ セクションで詳しく説明したように、テスト プラットフォームが適切な環境変数を使用して新しいホストを開始できるようになります。
カスタム ITestHostEnvironmentVariableProvider を登録するには、次の API を利用します。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddEnvironmentVariableProvider(
static serviceProvider => new CustomEnvironmentVariableForTestHost());
ファクトリは IServiceProvider を利用して、テスト プラットフォームで提供されるサービス スイートへのアクセスを取得します。
Von Bedeutung
API は登録された順序で呼び出されるため、登録のシーケンスは重要です。
ITestHostEnvironmentVariableProvider インターフェイスには、以下のメソッドと型が含まれます。
public interface ITestHostEnvironmentVariableProvider : ITestHostControllersExtension, IExtension
{
Task UpdateAsync(IEnvironmentVariables environmentVariables);
Task<ValidationResult> ValidateTestHostEnvironmentVariablesAsync(
IReadOnlyEnvironmentVariables environmentVariables);
}
public interface IEnvironmentVariables : IReadOnlyEnvironmentVariables
{
void SetVariable(EnvironmentVariable environmentVariable);
void RemoveVariable(string variable);
}
public interface IReadOnlyEnvironmentVariables
{
bool TryGetVariable(
string variable,
[NotNullWhen(true)] out OwnedEnvironmentVariable? environmentVariable);
}
public sealed class OwnedEnvironmentVariable : EnvironmentVariable
{
public IExtension Owner { get; }
public OwnedEnvironmentVariable(
IExtension owner,
string variable,
string? value,
bool isSecret,
bool isLocked);
}
public class EnvironmentVariable
{
public string Variable { get; }
public string? Value { get; }
public bool IsSecret { get; }
public bool IsLocked { get; }
}
ITestHostEnvironmentVariableProvider は、すべてのITestHostControllersExtension拡張機能のベースとして機能する の型です。 他のすべての拡張点と同様に、IExtension からも継承されます。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
この API の詳細に注意を払いましょう。
UpdateAsync: この更新 API は IEnvironmentVariables オブジェクトのインスタンスを提供し、そこから SetVariable または RemoveVariable のメソッドを呼び出すことができます。
SetVariable を使用する場合は、次の仕様を必要とする型 EnvironmentVariableのオブジェクトを渡す必要があります。
-
Variable: 環境変数の名前。 -
Value: 環境変数の値。 -
IsSecret: これは、TryGetVariableを使用してログを作成したりアクセスしたりしてはならない機密情報が環境変数に含まれているかどうかを示します。 -
IsLocked: これにより、他のITestHostEnvironmentVariableProvider拡張機能がこの値を変更できるかどうかが決まります。
ValidateTestHostEnvironmentVariablesAsync: このメソッドは、登録済みの UpdateAsync インスタンスのすべての ITestHostEnvironmentVariableProvider メソッドが呼び出された後に呼び出されます。 環境変数の正しいセットアップを確認できます。
IReadOnlyEnvironmentVariables を実装するオブジェクトを取得すると、TryGetVariable オブジェクト型を使用して特定の環境変数情報をフェッチする OwnedEnvironmentVariable メソッドが提供されます。 検証後、エラーの理由が含まれている ValidationResult を返します。
注
テスト プラットフォームは、既定で、SystemEnvironmentVariableProvider を実装して登録します。 このプロバイダーは、現在のすべての環境変数を読み込みます。 それは、最初に登録されたプロバイダーとして最初に実行され、他のすべての ITestHostEnvironmentVariableProvider ユーザー拡張機能に対して既定の環境変数へのアクセスが付与されます。
拡張機能が集中的な初期化を必要としており、ユーザーが async/await パターンを使用する必要がある場合には、Async extension initialization and cleanup を参照できます。 拡張点の間で状態を共有する必要がある場合は、CompositeExtensionFactory<T> セクションを参照できます。
ITestHostProcessLifetimeHandler 拡張拡張
ITestHostProcessLifetimeHandler はアウトプロセス拡張機能で、外部の観点からテスト ホスト プロセスを観察できます。 これにより、テスト対象のコードによって誘発される可能性のあるクラッシュやハングの影響を、拡張機能が受けないようにします。 この拡張点を利用すると、アーキテクチャ セクションで詳しく説明したように、テスト プラットフォームは新しいホストを開始するように求められます。
カスタム ITestHostProcessLifetimeHandler を登録するには、次の API を利用します。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddProcessLifetimeHandler(
static serviceProvider => new CustomMonitorTestHost());
ファクトリは IServiceProvider を利用して、テスト プラットフォームで提供されるサービス スイートへのアクセスを取得します。
Von Bedeutung
API は登録された順序で呼び出されるため、登録のシーケンスは重要です。
ITestHostProcessLifetimeHandler インターフェイスには、以下のメソッドが含まれます。
public interface ITestHostProcessLifetimeHandler : ITestHostControllersExtension
{
Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken);
Task OnTestHostProcessStartedAsync(
ITestHostProcessInformation testHostProcessInformation,
CancellationToken cancellation);
Task OnTestHostProcessExitedAsync(
ITestHostProcessInformation testHostProcessInformation,
CancellationToken cancellation);
}
public interface ITestHostProcessInformation
{
int PID { get; }
int ExitCode { get; }
bool HasExitedGracefully { get; }
}
ITestHostProcessLifetimeHandler は、すべてのITestHostControllersExtension拡張機能のベースとして機能する の型です。 他のすべての拡張点と同様に、IExtension からも継承されます。 そのため、他の拡張機能と同様に、IExtension.IsEnabledAsync API を使用してそれを有効または無効にすることができます。
この API の詳細に注意を払いましょう。
BeforeTestHostProcessStartAsync: このメソッドは、テスト プラットフォームがテスト ホストを開始する前に呼び出されます。
OnTestHostProcessStartedAsync: このメソッドは、テスト ホストの開始直後に呼び出されます。 このメソッドは、テスト ホスト プロセスの結果に関する重要な詳細を提供する ITestHostProcessInformation インターフェイスを実装するオブジェクトを提供します。
Von Bedeutung
このメソッドを呼び出しても、テスト ホストの実行は停止しません。 一時停止する必要がある場合は、インプロセス拡張機能を ITestApplicationLifecycleCallbacks として登録し、アウトプロセス拡張機能と同期する必要があります。
OnTestHostProcessExitedAsync: このメソッドは、テスト スイートの実行が完了したときに呼び出されます。 このメソッドは、テスト ホスト プロセスの結果に関する重要な詳細を伝える ITestHostProcessInformation インターフェイスに準拠したオブジェクトを提供します。
ITestHostProcessInformation インターフェイスでは、次の詳細が提供されます。
-
PID: テスト ホストのプロセス ID。 -
ExitCode: プロセスの終了コード。 この値は、OnTestHostProcessExitedAsyncメソッド内でのみ使用できます。OnTestHostProcessStartedAsyncメソッド内でアクセスしようとすると、例外が発生します。 -
HasExitedGracefully: テスト ホストがクラッシュしたかどうかを示すブール値。 true の場合は、テスト ホストが正常に終了しなかったことを示します。
拡張機能の実行順序
テスト プラットフォームは、テスト フレームワークと、インプロセスまたはアウトプロセスを操作できる任意の数の拡張機能で構成されます。 このドキュメントでは、機能が呼び出されるタイミングの想定について明確にするために、すべての潜在的な拡張ポイントの呼び出しのシーケンスについて説明します。
- ITestHostEnvironmentVariableProvider.UpdateAsync : アウトプロセス
- ITestHostEnvironmentVariableProvider.ValidateTestHostEnvironmentVariablesAsync : アウトプロセス
- ITestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync : アウトプロセス
- テスト ホスト プロセスの開始
- ITestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync : アウトプロセス、このイベントは競合条件によりインプロセス拡張機能のアクションが絡み合う可能性があります。
- ITestApplicationLifecycleCallbacks.BeforeRunAsync: インプロセス
- ITestSessionLifetimeHandler.OnTestSessionStartingAsync: インプロセス
- ITestFramework.CreateTestSessionAsync: インプロセス
- ITestFramework.ExecuteRequestAsync: インプロセス、このメソッドは 1 回以上呼び出すことができます。 この時点で、テスト フレームワークは IDataConsumer で利用できる情報を IMessageBus に送信します。
- ITestFramework.CloseTestSessionAsync: インプロセス
- ITestSessionLifetimeHandler.OnTestSessionFinishingAsync: インプロセス
- ITestApplicationLifecycleCallbacks.AfterRunAsync: インプロセス
- インプロセス クリーンアップでは、すべての拡張点で Dispose と IAsyncCleanableExtension を呼び出す必要があります。
- ITestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync : アウトプロセス
- アウトプロセス クリーンアップでは、すべての拡張点で Dispose と IAsyncCleanableExtension を呼び出す必要があります。
拡張機能ヘルパー
テスト プラットフォームには、拡張機能の実装を簡素化するためのヘルパー クラスおよびインターフェースのセットが用意されています。 これらのヘルパーは、開発プロセスを合理化し、拡張機能がプラットフォームの標準に準拠するように設計されています。
拡張機能の非同期初期化とクリーンアップ
ファクトリを介してテスト フレームワークと拡張機能を作成することは、同期コンストラクターを使用する標準の .NET オブジェクト作成メカニズムに準拠しています。 拡張機能が集中的な初期化 (ファイル システムまたはネットワークへのアクセスなど) を必要とする場合、コンストラクターは ではなく void を返すため、コンストラクタで Task パターンを使用することはできません。
そのため、テスト プラットフォームには、単純なインターフェイス経由で async/await パターンを使用して拡張機能を初期化するメソッドが用意されています。 対称性のために、拡張機能がシームレスに実装できるクリーンアップ用の非同期インターフェイスも提供されます。
public interface IAsyncInitializableExtension
{
Task InitializeAsync();
}
public interface IAsyncCleanableExtension
{
Task CleanupAsync();
}
IAsyncInitializableExtension.InitializeAsync: このメソッドは、作成ファクトリの後で確実に呼び出されます。
IAsyncCleanableExtension.CleanupAsync: このメソッドは、テスト セッションの終了時、既定の または DisposeAsync の前にDisposeは確実に呼び出されます。
Von Bedeutung
標準 Dispose メソッドと同様に、 CleanupAsync を複数回呼び出すことができます。 オブジェクトのCleanupAsyncメソッドが 2 回以上呼び出された場合、オブジェクトは、最初の呼び出しの後すべての呼び出しを無視する必要があります。 オブジェクトは、CleanupAsync メソッドが複数回呼び出される場合、例外をスローしてはなりません。
注
既定では、テスト プラットフォームは DisposeAsync (使用可能な場合) または Dispose (実装されている場合) を呼び出します。 テスト プラットフォームでは両方の Dispose メソッドが呼び出されるのではなく、実装されている場合には非同期メソッドが優先されることに注意することが重要です。
CompositeExtensionFactory<T>
拡張セクションで説明したように、このテスト プラットフォームでは、インプロセスとアウトプロセスの両方にカスタム拡張機能を組み込むためのインターフェースを実装できます。
各インターフェイスは特定の機能に対処します。ユーザーは .NET 設計に従って、このインターフェイスを特定のオブジェクトに実装します。 拡張機能自体は、対応するセクションで詳しく説明されているように、AddXXX の特定の登録 API TestHost または TestHostController の ITestApplicationBuilder オブジェクトを使用して登録できます。
ただし、2 つの拡張機能間で状態を共有する必要がある場合、異なるインターフェースを実装する異なるオブジェクトを実装して登録できるため、共有は困難なタスクになります。 支援がなければ、一方の拡張機能をもう一方の拡張機能に渡して情報を共有する方法が必要になるため、設計は複雑になります。
そのため、テスト プラットフォームでは、同じ型を使用して複数の拡張点を実装する高度なメソッドを用意して、データ共有を簡単なタスクにしています。 必要なのは、1 回のインターフェイス実装で使用するものと同じ API を使用して登録できる、CompositeExtensionFactory<T> を利用することです。
ITestSessionLifetimeHandler と IDataConsumer の両方を実装する型を例に考えてみましょう。 これは一般的なシナリオです。なぜなら、ユーザーはテスト フレームワークから情報を収集し、テスト セッションが終了したら、IMessageBus 内の ITestSessionLifetimeHandler.OnTestSessionFinishingAsync を使用してアーティファクトを送信することが多いからです。
通常、行うべきことはインターフェイスの実装です。
internal class CustomExtension : ITestSessionLifetimeHandler, IDataConsumer, ...
{
...
}
型に対して CompositeExtensionFactory<CustomExtension> を作成したら、IDataConsumer のオーバーロードを提供する ITestSessionLifetimeHandler API と CompositeExtensionFactory<T> API の両方に登録できます。
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
var factory = new CompositeExtensionFactory<CustomExtension>(serviceProvider => new CustomExtension());
builder.TestHost.AddTestSessionLifetimeHandle(factory);
builder.TestHost.AddDataConsumer(factory);
ファクトリ コンストラクターは、IServiceProvider を使用して、テスト プラットフォームによって提供されるサービスにアクセスします。
テスト プラットフォームは、複合拡張機能のライフサイクルを管理する役割を担います。
インプロセス拡張機能とアウトプロセス拡張機能の両方に対するテスト プラットフォームのサポートにより、拡張点を任意に組み合わせることができないことに注意してください。 拡張機能の作成と利用はホストの種類に依存します。つまり、インプロセス (TestHost) とアウトプロセス (TestHostController) の拡張機能のみグループ化できます。
次の組み合わせが可能です。
-
ITestApplicationBuilder.TestHostの場合は、IDataConsumerとITestSessionLifetimeHandlerを組み合わせることができます。 -
ITestApplicationBuilder.TestHostControllersの場合は、ITestHostEnvironmentVariableProviderとITestHostProcessLifetimeHandlerを組み合わせることができます。
.NET