MSTest には、テスト クラスとテスト メソッドの明確に定義されたライフサイクルが用意されており、テスト実行のさまざまな段階でセットアップ操作と破棄操作を実行できます。 ライフサイクルを理解することは、効率的なテストを記述し、一般的な落とし穴を回避するのに役立ちます。
ライフサイクルの概要
ライフサイクルは、最高レベル (アセンブリ) から最下位レベル (テスト メソッド) に実行される 4 つのステージにグループ化されます。
- アセンブリ レベル: テスト アセンブリの読み込みとアンロード時に 1 回実行されます
- クラス レベル: テスト クラスごとに 1 回実行
- グローバル テスト レベル: アセンブリ内のすべてのテスト メソッドの前後に実行されます
- テスト レベル: 各テスト メソッドに対して実行されます (パラメーター化されたテストの各データ行を含む)
アセンブリ レベルのライフサイクル
アセンブリ ライフサイクル メソッドは、テスト アセンブリの読み込みとアンロード時に 1 回実行されます。 データベースの初期化やサービスの起動など、コストの高い 1 回限りのセットアップに使用します。
AssemblyInitialize および AssemblyCleanup
[TestClass]
public class AssemblyLifecycleExample
{
private static IHost? _host;
[AssemblyInitialize]
public static async Task AssemblyInit(TestContext context)
{
// Runs once before any tests in the assembly
_host = await StartTestServerAsync();
context.WriteLine("Test server started");
}
[AssemblyCleanup]
public static async Task AssemblyCleanup(TestContext context)
{
// Runs once after all tests complete
// TestContext parameter available in MSTest 3.8+
if (_host != null)
{
await _host.StopAsync();
}
}
private static Task<IHost> StartTestServerAsync()
{
// Server initialization
return Task.FromResult<IHost>(null!);
}
}
Requirements
- メソッドは〘
public static - 戻り値の型:
void、Task、またはValueTask(MSTest v3.3 以降) -
AssemblyInitializeには、1 つのTestContextパラメーターが必要です -
AssemblyCleanup0 個のパラメーターまたは 1 つのTestContextパラメーターを受け取ります (MSTest 3.8 以降) - アセンブリごとに許可される各属性の 1 つだけ
- でマークされたクラスに存在する必要があります
[TestClass]
クラス レベルのライフサイクル
クラス ライフサイクル メソッドは、テスト クラスごとに、そのクラスのすべてのテスト メソッドの前後に 1 回実行されます。 これらは、クラス内のテスト間で共有されるセットアップに使用します。
ClassInitialize および ClassCleanup
[TestClass]
public class ClassLifecycleExample
{
private static HttpClient? _client;
[ClassInitialize]
public static void ClassInit(TestContext context)
{
// Runs once before any tests in this class
_client = new HttpClient
{
BaseAddress = new Uri("https://api.example.com")
};
}
[ClassCleanup]
public static void ClassCleanup()
{
// Runs after all tests in this class complete
_client?.Dispose();
}
[TestMethod]
public async Task GetUsers_ReturnsSuccess()
{
HttpResponseMessage response = await _client!.GetAsync("/users");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Requirements
- メソッドは〘
public static - 戻り値の型:
void、Task、またはValueTask(MSTest v3.3 以降) -
ClassInitializeには、1 つのTestContextパラメーターが必要です -
ClassCleanup0 個のパラメーターまたは 1 つのTestContextパラメーターを受け取ります (MSTest 3.8 以降) - クラスごとに許可される各属性の 1 つだけ
継承の振る舞い
ClassInitializeを使って、派生クラスがInheritanceBehaviorを実行するかどうかを制御します。
[TestClass]
public class BaseTestClass
{
[ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)]
public static void BaseClassInit(TestContext context)
{
// Runs before each derived class's tests
}
}
[TestClass]
public class DerivedTestClass : BaseTestClass
{
[TestMethod]
public void DerivedTest()
{
// BaseClassInit runs before this class's tests
}
}
| InheritanceBehavior | Description |
|---|---|
None (既定値) |
宣言するクラスに対してのみ実行を初期化する |
BeforeEachDerivedClass |
各派生クラスの前に実行を初期化する |
ヒント
関連するアナライザー:
-
MSTEST0010 - 署名
ClassInitialize検証します。 -
MSTEST0011 - 署名
ClassCleanup検証します。 -
MSTEST0034 -
ClassCleanupBehavior.EndOfClassの使用をお勧めします。
グローバルテストレベルライフサイクル
注
グローバル テスト ライフサイクル属性は、MSTest 3.10.0 で導入されました。
グローバル テスト ライフサイクル メソッドは、各テスト クラスにコードを追加する必要なく、アセンブリ全体 のすべての テスト メソッドの前後に実行されます。
GlobalTestInitialize および GlobalTestCleanup
[TestClass]
public class GlobalTestLifecycleExample
{
[GlobalTestInitialize]
public static void GlobalTestInit(TestContext context)
{
// Runs before every test method in the assembly
context.WriteLine($"Starting test: {context.TestName}");
}
[GlobalTestCleanup]
public static void GlobalTestCleanup(TestContext context)
{
// Runs after every test method in the assembly
context.WriteLine($"Finished test: {context.TestName}");
}
}
Requirements
- メソッドは〘
public static - 戻り値の型:
void、Task、またはValueTask -
TestContextパラメーターは正確に 1 つである必要があります。 - でマークされたクラスに存在する必要があります
[TestClass] - これらの属性を持つ複数のメソッドがアセンブリ全体で許可される
注
複数の GlobalTestInitialize または GlobalTestCleanup メソッドが存在する場合、実行順序は保証されません。
TimeoutAttributeは、GlobalTestInitializeメソッドではサポートされていません。
ヒント
関連アナライザー: MSTEST0050 - グローバル テスト フィクスチャ メソッドを検証します。
試験段階のライフサイクル
テスト レベルのライフサイクルは、すべてのテスト メソッドに対して実行されます。 パラメーター化されたテストの場合、ライフサイクルはデータ行ごとに実行されます。
セットアップ フェーズ
テストごとのセットアップには、 TestInitialize またはコンストラクターを使用します。
[TestClass]
public class TestLevelSetupExample
{
private Calculator? _calculator;
public TestLevelSetupExample()
{
// Constructor runs before TestInitialize
// Use for simple synchronous initialization
}
[TestInitialize]
public async Task TestInit()
{
// Runs before each test method
// Supports async, attributes like Timeout
_calculator = new Calculator();
await _calculator.InitializeAsync();
}
[TestMethod]
public void Add_TwoNumbers_ReturnsSum()
{
int result = _calculator!.Add(2, 3);
Assert.AreEqual(5, result);
}
}
コンストラクターと TestInitialize:
| 特徴 | コンストラクター | TestInitialize |
|---|---|---|
| 非同期サポート | いいえ | イエス |
| タイムアウトのサポート | いいえ | はい ( [Timeout] 属性を使用) |
| 実行順序 | First | コンストラクタ処理後 |
| 継承 | 次に派生した Base | 次に派生した Base |
| 例外の動作 | クリーンアップと Dispose が実行されない (インスタンスが存在しない) | クリーンアップとDisposeはまだ実行中です。 |
ヒント
どの方法を使用する必要がありますか? コンストラクターは、不変性を強制し、テスト クラスの推論を容易にする readonly フィールドを使用できるため、一般的に推奨されます。 非同期初期化またはタイムアウトのサポートが必要な場合は、 TestInitialize を使用します。
また、両方の方法を組み合わせることもできます。 readonly フィールドの単純な同期初期化にはコンストラクターを使用し、それらのフィールドに依存する追加の非同期セットアップには TestInitialize を使用します。
必要に応じて、コード アナライザーを有効にして、一貫したアプローチを適用できます。
- MSTEST0019 - コンストラクターよりも TestInitialize メソッドを優先する
- MSTEST0020 - TestInitialize メソッドよりもコンストラクターを優先する
実行フェーズ
テスト メソッドは、セットアップが完了した後に実行されます。
asyncテスト メソッドの場合、MSTest は返されたTaskまたはValueTaskを待機します。
Warnung
非同期テスト メソッドには、既定では SynchronizationContext はありません。 これは、UI スレッドで実行される UWP および WinUI の UITestMethod テストには適用されません。
クリーンアップ フェーズ
テストごとのクリーンアップには、 TestCleanup または IDisposable/IAsyncDisposable を使用します。
[TestClass]
public class TestLevelCleanupExample
{
private HttpClient? _client;
[TestInitialize]
public void TestInit()
{
_client = new HttpClient();
}
[TestCleanup]
public void TestCleanup()
{
if (_client != null)
{
_client.Dispose();
}
}
[TestMethod]
public async Task GetData_ReturnsSuccess()
{
HttpResponseMessage response = await _client!.GetAsync("https://example.com");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
クリーンアップの実行順序 (基本に派生):
-
TestCleanup(派生クラス) -
TestCleanup(基底クラス) -
DisposeAsync(実装されている場合) -
Dispose(実装されている場合)
ヒント
必要に応じて、コード アナライザーを有効にして、一貫したクリーンアップ アプローチを適用できます。
- MSTEST0021 - TestCleanup メソッドよりも Dispose を優先する
- MSTEST0022 - Dispose メソッドよりも TestCleanup を優先する
.NET コード分析ルールなど、MSTest 以外のアナライザーが有効になっている場合は、テスト クラスが破棄可能なリソースを所有している場合に破棄パターンを実装することを提案する CA1001 が表示されることがあります。 これは予期される動作であり、アナライザーのガイダンスに従う必要があります。
テスト段階の順序を完了する
- テスト クラス (コンストラクター) のインスタンスを作成する
- プロパティ
TestContext設定する (存在する場合) -
GlobalTestInitializeメソッドを実行する -
TestInitializeメソッドを基底から派生クラスまで実行する - テスト メソッドの実行
- 結果で
TestContextを更新する (Outcomeプロパティなど) -
TestCleanupメソッドを実行する (派生クラスから基本クラスへ) -
GlobalTestCleanupメソッドを実行する -
DisposeAsyncを実行する (実装されている場合) -
Disposeを実行する (実装されている場合)
ヒント
関連するアナライザー:
-
MSTEST0008 - 署名
TestInitialize検証します。 -
MSTEST0009 - 署名
TestCleanup検証します。 - MSTEST0063 - テスト クラスコンストラクターを検証します。
ベスト プラクティス
適切なスコープを使用する: 冗長な作業を回避するために、セットアップを最も高いレベルに配置します。
セットアップを高速に保つ: 実行時間の長いセットアップは、すべてのテストに影響します。 コストの高いリソースについては、遅延初期化を検討してください。
適切にクリーンアップする: 常にリソースをクリーンアップして、テストの干渉とメモリ リークを防ぎます。
非同期を正しく処理する: 戻り値の型
async Taskを使用し、async voidではなく非同期ライフサイクル メソッドにしてください。テストの分離を検討する: 各テストは独立している必要があります。 テスト間で変更可能な状態を共有しないようにします。
GlobalTest を慎重に使用する: グローバル ライフサイクル メソッドは、すべてのテストに対して実行されるため、軽量に保ちます。
こちらも参照ください
.NET