web-dev-qa-db-ja.com

xUnit.net:グローバル設定+分解?

この質問は、単体テストフレームワーク xUnit.net についてです。

テストを実行する前にいくつかのコードを実行する必要があります。また、すべてのテストを実行した後にいくつかのコードを実行する必要があります。グローバルな初期化および終了コードを示すために、何らかの種類の属性またはマーカーインターフェイスがあるはずだと思っていましたが、見つかりませんでした。

あるいは、xUnitをプログラムで呼び出す場合、次のコードで目的を達成することもできます。

static void Main()
{
    try
    {
        MyGlobalSetup();
        RunAllTests();  // What goes into this method?
    }
    finally
    {
        MyGlobalTeardown();
    }
}

誰かがグローバルなセットアップ/ティアダウンコードを宣言的またはプログラム的に実行する方法についてのヒントを提供できますか?

75
Codism

私の知る限り、xUnitにはグローバルな初期化/ティアダウン拡張ポイントがありません。ただし、作成するのは簡単です。 IDisposableを実装する基本テストクラスを作成し、コンストラクターで初期化を行い、IDisposable.Disposeメソッドで分解を行うだけです。これは次のようになります。

public abstract class TestsBase : IDisposable
{
    protected TestsBase()
    {
        // Do "global" initialization here; Called before every test method.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Called after every test method.
    }
}

public class DummyTests : TestsBase
{
    // Add test methods
}

ただし、呼び出しごとに基本クラスのセットアップと分解コードが実行されます。あまり効率的ではないので、これはあなたが望むものではないかもしれません。より最適化されたバージョンでは、IClassFixture<T>インターフェイスを使用して、グローバル初期化/ティアダウン機能が1回だけ呼び出されるようにします。このバージョンでは、テストクラスから基本クラスを拡張するのではなく、IClassFixture<T>インターフェイスを実装します。ここで、Tはフィクスチャクラスを指します。

using Xunit;

public class TestsFixture : IDisposable
{
    public TestsFixture ()
    {
        // Do "global" initialization here; Only called once.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Only called once.
    }
}

public class DummyTests : IClassFixture<TestsFixture>
{
    public void SetFixture(TestsFixture data)
    {
    }
}

このwillの結果、TestsFixtureのコンストラクターは、テスト中のすべてのクラスに対して1回だけ実行されます。したがって、2つの方法のどちらを正確に選択するかによって異なります。

82
Erik Schierboom

私は同じ答えを探していましたが、この時点でxUnitのドキュメントは、クラスまたはクラスのグループレベルで幅広いセットアップ/ティアダウン機能を開発者に提供するクラスフィクスチャとコレクションフィクスチャの実装方法に関して非常に役立ちます。これは、Geir Sagbergの回答と一致しており、スケルトンの適切な実装により、どのように見えるべきかを示しています。

https://xunit.github.io/docs/shared-context.html

コレクションフィクスチャ使用する場合:単一のテストコンテキストを作成し、それを複数のテストクラスのテスト間で共有し、テストクラスのすべてのテストが終了した後にクリーンアップする場合。

複数のテストクラス間でフィクスチャオブジェクトを共有したい場合があります。クラスフィクスチャに使用されるデータベースの例は素晴らしい例です。データベースを一連のテストデータで初期化し、そのテストデータを複数のテストクラスで使用するためにそのままにしておくことができます。 xUnit.netのコレクションフィクスチャ機能を使用して、単一のオブジェクトインスタンスを複数のテストクラスのテスト間で共有できます。

コレクションフィクスチャを使用するには、次の手順を実行する必要があります。

フィクスチャクラスを作成し、フィクスチャクラスコンストラクタにスタートアップコードを配置します。フィクスチャクラスでクリーンアップを実行する必要がある場合は、フィクスチャクラスにIDisposableを実装し、Dispose()メソッドにクリーンアップコードを配置します。コレクション定義クラスを作成し、[CollectionDefinition]属性で装飾し、テストコレクションを識別する一意の名前を付けます。 ICollectionFixture <>をコレクション定義クラスに追加します。テストコレクション定義クラスの[CollectionDefinition]属性に指定した一意の名前を使用して、コレクションの一部となるすべてのテストクラスに[Collection]属性を追加します。テストクラスがフィクスチャインスタンスにアクセスする必要がある場合、コンストラクター引数として追加すると、自動的に提供されます。以下に簡単な例を示します。

public class DatabaseFixture : IDisposable
{
    public DatabaseFixture()
    {
        Db = new SqlConnection("MyConnectionString");

        // ... initialize data in the test database ...
    }

    public void Dispose()
    {
        // ... clean up test data from the database ...
    }

    public SqlConnection Db { get; private set; }
}

[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
    // This class has no code, and is never created. Its purpose is simply
    // to be the place to apply [CollectionDefinition] and all the
    // ICollectionFixture<> interfaces.
}

[Collection("Database collection")]
public class DatabaseTestClass1
{
    DatabaseFixture fixture;

    public DatabaseTestClass1(DatabaseFixture fixture)
    {
        this.fixture = fixture;
    }
}

[Collection("Database collection")]
public class DatabaseTestClass2
{
    // ...
}

xUnit.netは、コレクションフィクスチャをクラスフィクスチャとほぼ同じ方法で処理しますが、コレクションフィクスチャオブジェクトのライフタイムが長くなります。コレクション内のテストクラスでテストが実行される前に作成され、クリーニングされません。コレクション内のすべてのテストクラスの実行が完了するまで。

テストコレクションは、IClassFixture <>で装飾することもできます。 xUnit.netはこれを、テストコレクション内の各テストクラスがクラスフィクスチャで装飾されているかのように扱います。

テストコレクションは、xUnit.netを並行して実行する場合のテストの実行方法にも影響します。詳細については、テストの並列実行を参照してください。

重要な注意:フィクスチャは、それらを使用するテストと同じアセンブリになければなりません。

36
Larry Smith

簡単で簡単な解決策があります。 Fody.ModuleInitプラグインを使用する

https://github.com/Fody/ModuleInit

これはnugetパッケージであり、インストールすると、ModuleInitializer.csという新しいファイルがプロジェクトに追加されます。ここには、ビルド後にアセンブリに織り込まれ、アセンブリがロードされてすぐに実行される前に実行される静的メソッドが1つあります。

これを使用して、購入したライブラリのソフトウェアライセンスのロックを解除します。私はいつも各テストでライセンスのロックを解除するのを忘れていましたし、それをロック解除する基本クラスからテストを派生するのを忘れていました。このライブラリを書いた明るい火花は、ライセンスがロックされていると伝える代わりに、微妙な数値エラーを導入しました。ライブラリを正しくロック解除したかどうかはわかりません。だから今私のモジュールの初期化のようになります

/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the Assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
    /// <summary>
    /// Initializes the module.
    /// </summary>
    public static void Initialize()
    {
            SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
    }
}

このアセンブリに配置されるすべてのテストでは、それらのライセンスが正しくロック解除されます。

11
bradgonesurfing

SetUp/TearDown-codeを複数のクラス間で共有するには、xUnitの CollectionFixture を使用できます。

見積もり:

コレクションフィクスチャを使用するには、次の手順を実行する必要があります。

  • フィクスチャクラスを作成し、フィクスチャクラスコンストラクタにスタートアップコードを配置します。
  • フィクスチャクラスでクリーンアップを実行する必要がある場合は、フィクスチャクラスにIDisposableを実装し、Dispose()メソッドにクリーンアップコードを配置します。
  • コレクション定義クラスを作成し、[CollectionDefinition]属性で装飾し、テストコレクションを識別する一意の名前を付けます。
  • ICollectionFixture <>をコレクション定義クラスに追加します。
  • テストコレクション定義クラスの[CollectionDefinition]属性に指定した一意の名前を使用して、コレクションの一部となるすべてのテストクラスに[Collection]属性を追加します。
  • テストクラスがフィクスチャインスタンスにアクセスする必要がある場合、コンストラクター引数として追加すると、自動的に提供されます。
10
Geir Sagberg