web-dev-qa-db-ja.com

xUnit.netでのすべてのテストの前後にコードを1回実行する

TL; DR-MSTestのAssemblyInitializeに相当するxUnitを探しています(別名、私が気に入っている1つの機能)。

具体的には、他の依存関係なしで実行できるようにしたいSeleniumスモークテストがあるため、探しています。私のためにIisExpressを起動し、処分時にそれを殺すフィクスチャがあります。しかし、すべてのテストの前にこれを行うと、ランタイムが非常に大きくなります。

このコードをテストの開始時に1回トリガーし、最後に破棄(プロセスのシャットダウン)したいと思います。どうすればそれを行うことができますか?

「現在実行されているテストの数」のようなものにプログラムでアクセスできる場合でも、何かを把握できます。

61
George Mauer

2015年11月現在、xUnit 2はリリースされているため、テスト間で機能を共有する標準的な方法があります。文書化されています ここ

基本的に、フィクスチャを実行するクラスを作成する必要があります。

    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属性を持つダミークラス。このクラスにより、Xunitはテストコレクションを作成でき、コレクションのすべてのテストクラスに対して指定されたフィクスチャを使用します。

    [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;
        }
    }

各テストクラスでどのテストコレクションが属するかを宣言する必要があるため、MsTests AssemblyInitializeよりも少し冗長ですが、よりモジュール化も可能です(MsTestsでは、クラスにTestClassを配置する必要があります)

注:サンプルは documentation から取得されています。

42
gwenzek

静的フィールドを作成し、ファイナライザーを実装します。

XUnitがAppDomainを作成してテストアセンブリを実行し、終了時にアンロードするという事実を使用できます。アプリドメインをアンロードすると、ファイナライザーが実行されます。

この方法を使用して、IISExpressを開始および停止しています。

public sealed class ExampleFixture
{
    public static ExampleFixture Current = new ExampleFixture();

    private ExampleFixture()
    {
        // Run at start
    }

    ~ExampleFixture()
    {
        Dispose();
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);

        // Run at end
    }        
}

編集:テストでExampleFixture.Currentを使用してフィクスチャにアクセスします。

22
Jared Kells

今日のフレームワークではできません。これは2.0で計画されている機能です。

この機能を2.0より前に機能させるには、フレームワークで大幅な再アーキテクチャを実行するか、独自の特別な属性を認識する独自のランナーを作成する必要があります。

16
Brad Wilson

Assembly Initializeでコードを実行するには、これを行うことができます(xUnit 2.3.1でテスト済み)

using Xunit.Abstractions;
using Xunit.Sdk;

[Assembly: Xunit.TestFramework("MyNamespace.MyClassName", "MyAssemblyName")]

namespace MyNamespace
{   
   public class MyClassName : XunitTestFramework
   {
      public MyClassName(IMessageSink messageSink)
        :base(messageSink)
      {
        // Place initialization code here
      }

      public new void Dispose()
      {
        // Place tear down code here
        base.Dispose();
      }
   }
}

https://github.com/xunit/samples.xunit/tree/master/AssemblyFixtureExample も参照してください

9
Rolf Kristensen

AssemblyFixtureNuGet )を使用します。

オブジェクトの存続期間をテストアセンブリとして使用するIAssemblyFixture<T>を置き換えるIClassFixture<T>インターフェイスを提供します。

例:

public class Singleton { }

public class TestClass1 : IAssemblyFixture<Singleton>
{
  readonly Singletone _Singletone;
  public TestClass1(Singleton singleton)
  {
    _Singleton = singleton;
  }

  [Fact]
  public void Test1()
  {
     //use singleton  
  }
}

public class TestClass2 : IAssemblyFixture<Singleton>
{
  readonly Singletone _Singletone;
  public TestClass2(Singleton singleton)
  {
    //same singleton instance of TestClass1
    _Singleton = singleton;
  }

  [Fact]
  public void Test2()
  {
     //use singleton  
  }
}
1
Shimmy

ビルドツールはそのような機能を提供していますか?

Java world、ビルドツールとして Maven を使用する場合、適切な ビルドライフサイクルのフェーズ を使用します。たとえば、あなたの場合(Seleniumのようなツールを使用した受け入れテスト)、pre-integration-testおよびpost-integration-test自分の前/後にwebappを開始/停止するフェーズintegration-tests。

環境に同じメカニズムを設定できると確信しています。

0
Vincent

IUseFixtureインターフェイスを使用して、これを実現できます。また、すべてのテストはTestBaseクラスを継承する必要があります。テストから直接OneTimeFixtureを使用することもできます。

public class TestBase : IUseFixture<OneTimeFixture<ApplicationFixture>>
{
    protected ApplicationFixture Application;

    public void SetFixture(OneTimeFixture<ApplicationFixture> data)
    {
        this.Application = data.Fixture;
    }
}

public class ApplicationFixture : IDisposable
{
    public ApplicationFixture()
    {
        // This code run only one time
    }

    public void Dispose()
    {
        // Here is run only one time too
    }
}

public class OneTimeFixture<TFixture> where TFixture : new()
{
    // This value does not share between each generic type
    private static readonly TFixture sharedFixture;

    static OneTimeFixture()
    {
        // Constructor will call one time for each generic type
        sharedFixture = new TFixture();
        var disposable = sharedFixture as IDisposable;
        if (disposable != null)
        {
            AppDomain.CurrentDomain.DomainUnload += (sender, args) => disposable.Dispose();
        }
    }

    public OneTimeFixture()
    {
        this.Fixture = sharedFixture;
    }

    public TFixture Fixture { get; private set; }
}

編集:新しいフィクスチャが各テストクラスに対して作成する問題を修正します。

0
Khoa Le