この記事では、テスト フレームワーク自体以外の MTP の機能拡張ポイントについて説明します。 テスト フレームワークの作成については、「 テスト フレームワークの構築」を参照してください。
完全な拡張ポイントの概要とプロセス内/プロセス外の概念については、「 カスタム拡張機能の作成」を参照してください。
機能拡張ポイント
テスト プラットフォームには、プラットフォームとテスト フレームワークの動作をカスタマイズできる追加の拡張ポイントが用意されています。 これらの拡張ポイントは省略可能であり、テスト エクスペリエンスを強化するために使用できます。
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 は、テスト フレームワークおよびその拡張機能によって IMessageBus にプッシュされる 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 をプッシュする能力は、コンシューマーが種類自体を切り替えることで、それを正しい種類にキャストし、特定の情報にアクセスする必要があることを意味します。
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
複合拡張点内で ITestHostProcessLifetimeHandler と組み合わせて IDataConsumer を使用する場合は、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: このメソッドは、少なくとも1回、テストセッションの終了時に、既定の または 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> を作成したら、CompositeExtensionFactory<T> のオーバーロードを提供する IDataConsumer API と ITestSessionLifetimeHandler 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