コンテキストでのテスト実行シナリオ
データ駆動型テストの最も単純なシナリオは次のようになります。
- テストのコレクション用のデータベースを作成します
- データベースを最新バージョンに更新します(オプション)
- 自動テストを実行する
- データベースを削除
データベースを更新する必要がある場合については、ここでは詳しく説明しません。 これらの技術的な詳細は、この記事では重要ではありません。 ただし、ADO.NETでは、GOを含むスクリプトを実行できないことに注意してください。 スクリプトを自動的に実行する場合は、各スクリプトを個別にロールできるようにシステムを構築します。 SQL Server管理オブジェクト(SMO)ライブラリでさえGOスクリプトを破壊し、個々の部分を実行します(検証済み)。 したがって、この記事では2番目の段落は考慮されません。 XUnitは残りの部分を支援します。 xUnitコンテキストの一般的な概念の説明については、それらのドキュメントを参照してください。
フィクスチャは、単一のスイートからテストを実行する前に作成されたクラスです。 スイートをテスト付きのクラスと呼んで、タウタロジーが受信されないようにします。 コレクションは、スイートのグループを記述するクラスですが、テストの実行中に作成されることはありません。 説明のみを目的としています。 これは、以下の例で示されます。
ライフサイクルフィクスチャとコレクション
GitHubで例を見つけることができます。 xUnit 2.0以降、作成者はIUseFixtureをICollectionFixtureおよびIClassFixtureに置き換えました。
xUnitがクラスをインスタンス化する方法を示すために、3つのスイートを作成しました。 それらの2つは同じコンテキストで実行する必要があります。
public class CollectionFixture : IDisposable { public CollectionFixture() public void Dispose() } public class ClassFixture : IDisposable { public ClassFixture() public void Dispose() } [CollectionDefinition("ContextOne")] public class TestCollection : ICollectionFixture<CollectionFixture> { public TestCollection() // TestCollection is never instantiated } [Collection("ContextOne")] public class TestContainerOne : IClassFixture<ClassFixture>, IDisposable { public TestContainerOne() [Fact] public void TestOne() [Fact] public void TestTwo() public void Dispose() } [Collection("ContextOne")] public class TestContainerTwo : IDisposable { public TestContainerTwo() [Fact] public void TestOne() public void Dispose() } public class TestContainerThree { [Fact] public void TestOne() }
出力ウィンドウの行を次のように表示するには、テストをデバッグモードで実行する必要があります。 私は、xUnitでテストを実行する際のパパレリズムについて1つの点に注目したいと思います。 デフォルトでは、テストは同じスイートまたはコレクション(存在する場合)内で同期的に実行されます。 それ以外の場合、テストは並行して実行されます。 詳細については、 こちらをご覧ください 。 したがって、実際には、出力はコンピューターによってわずかに異なる場合がありますが、より大きな兆候のためにソートしました。
CollectionFixture : ctor ClassFixture : ctor TestContainerOne : ctor TestContainerOne : TestOne TestContainerOne : disposed TestContainerOne : ctor TestContainerOne : TestTwo TestContainerOne : disposed ClassFixture : disposed TestContainerTwo : ctor TestContainerTwo : TestOne TestContainerTwo : disposed CollectionFixture : disposed TestContainerThree : TestOne
したがって、複数のテストを1つのコンテキストにグループ化するには、ICollectionFixtureを使用できます。 同時に、IClassFixtureは特定のスイートの環境設定を調整できます。 スイートコンストラクターは、テストの数に関係なく、個々のテストごとに呼び出されることに注意することが重要です。 破棄では、対応するスコープ(テスト、スイート、またはコレクション)のクリーニングコードを持つことが合理的です。
実装の詳細
上記のスクリプトを実行するクラスを作成し、特定のタスクに応じてICollectionFixtureまたはIClassFixtureを使用してテストに添付できることは明らかです。 この例では、テストの前にデータベースを復元するコレクションを使用し、Dispose()でそれを削除します。
このアプローチの次の問題に注意する価値があります。
- バックアップからデータベースを復元する場合、ファイルに関する内部情報が使用されます。 テストの並列実行の場合、復元されたデータベース内の名前の競合により、テストが失敗する可能性があります。 この問題を解決するには、ファイルを移動してデータベースを復元する必要があります。 これはGitHubの例にあります。
- 一般に、データベースは異なる数のファイル(データ、ログ、ファイルストリームなど)を持つことができます。 この状況は正しく処理する必要があります。 ただし、この記事の目的上、必要なのはデータとログのみです。 残りのファイルは、部分回復( PARTIAL )を使用して無視されます。
以下は、データベースを復元および削除するためのT-SQLの例です。
IF NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = '<DBNAME>') BEGIN DECLARE @Table TABLE ( LogicalName VARCHAR(128) , [PhysicalName] VARCHAR(128) , [Type] VARCHAR , [FileGroupName] VARCHAR(128) , [Size] VARCHAR(128) , [MaxSize] VARCHAR(128) , [FileId] VARCHAR(128) , [CreateLSN] VARCHAR(128) , [DropLSN] VARCHAR(128) , [UniqueId] VARCHAR(128) , [ReadOnlyLSN] VARCHAR(128) , [ReadWriteLSN] VARCHAR(128) , [BackupSizeInBytes] VARCHAR(128) , [SourceBlockSize] VARCHAR(128) , [FileGroupId] VARCHAR(128) , [LogGroupGUID] VARCHAR(128) , [DifferentialBaseLSN] VARCHAR(128) , [DifferentialBaseGUID] VARCHAR(128) , [IsReadOnly] VARCHAR(128) , [IsPresent] VARCHAR(128) , [TDEThumbprint] VARCHAR(128) ) INSERT INTO @Table EXEC ( 'RESTORE FILELISTONLY FROM DISK = ''<PATH_TO_BACKUP_FILE>''') DECLARE @LogicalNameData varchar(128), @LogicalNameLog varchar(128) SET @LogicalNameData=(SELECT LogicalName FROM @Table WHERE Type='D') SET @LogicalNameLog=(SELECT LogicalName FROM @Table WHERE Type='L') EXEC ('RESTORE DATABASE [<DBNAME>] FROM DISK = ''<PATH_TO_BACKUP_FILE>'' WITH MOVE '''+@LogicalNameData+''' TO ''<PATH>\<DBNAME>_Data.mdf'', MOVE '''+@LogicalNameLog+''' TO ''<PATH>\<DBNAME>_Log.ldf'', REPLACE, PARTIAL' ) END
ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE DROP DATABASE [{0}]
たとえば、GitHubに関するいくつかのコメント。 データベースコンテキストをユニバーサルにするフィクスチャを作成するには、接続文字列とバックアップファイルへのパスをコンストラクタに渡す必要があります。 SqlConnectionStringBuilderクラスを使用して、他のスクリプトの接続文字列内のデータベース名を決定できます。 作成および削除スクリプトは、[master]データベースのコンテキストで実行する必要があります。 特定のテストセットの後でデータベースを削除する必要がある場合は、Dispose()を呼び出して強制的に削除します。 もちろん、xUnit自体によって呼び出されますが、非決定的であり、おそらく、データベースの競合のためにテストが少し早く失敗する可能性があります。