xUnit.net - 在所有测试之前和之后运行一次代码

Geo*_*uer 63 c# automated-tests xunit.net

TL; DR - 我正在寻找xUnit相当于MSTest AssemblyInitialize(也就是我喜欢的ONE功能).

具体来说,我正在寻找它,因为我有一些Selenium冒烟测试,我希望能够在没有其他依赖项的情况下运行.我有一个Fixture,它将为我启动IisExpress并在处置时杀死它.但是在每次测试之前执行此操作会大大增加运行时间.

我想在测试开始时触发一次这个代码,并在最后处理它(关闭进程).我怎么能这样做呢?

即使我可以通过编程方式访问诸如"当前正在运行多少个测试"之类的东西,我也可以解决一些问题.

gwe*_*zek 45

截至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; }
    }
Run Code Online (Sandbox Code Playgroud)

带有CollectionDefinition属性的虚拟类.此类允许Xunit创建测试集合,并将使用给定的fixture来处理集合的所有测试类.

    [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.
    }
Run Code Online (Sandbox Code Playgroud)

然后,您需要在所有测试类上添加集合名称.测试类可以通过构造函数接收fixture.

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

        public DatabaseTestClass1(DatabaseFixture fixture)
        {
            this.fixture = fixture;
        }
    }
Run Code Online (Sandbox Code Playgroud)

它比MsTests更冗长,AssemblyInitialize因为你必须在每个测试类中声明它所属的测试集合,但它也更加可调(并且使用MsTests,你仍然需要在你的类上放置一个TestClass)

注意:样本来自文档.

  • [Collection]属性可防止测试并行运行.是否有其他方法可以在xUnit中进行全局初始化/拆除,允许并行运行测试? (8认同)
  • 当我读到这个:"... //这个类没有代码,永远不会创建......"然后我真的更喜欢Microsofts实现的AssemblyInitialize.更优雅. (7认同)
  • @Elisabeth我刚刚使用了它,并将`[CollectionDefinition("Database collection")]`属性和`ICollectionFixture <DatabaseFixture>`接口添加到`DatabaseFixture`类中,一切正常.它删除了一个空类,对我来说似乎更干净! (7认同)
  • 我知道这已经很旧了,但是如何在单个测试类上拥有多个集合?我希望它运行数据库设置代码以及我的映射代码。 (2认同)

Jar*_*lls 22

创建静态字段并实现终结器.

您可以使用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
    }        
}
Run Code Online (Sandbox Code Playgroud)

编辑:ExampleFixture.Current在测试中使用夹具.

  • 这只是轶事,但是自发布此答案以来,我的构建服务器已经运行了一个月,并且使用此方法完成了300次构建,并且工作正常。 (2认同)

Bra*_*son 16

今天在框架中不可能做到.这是针对2.0计划的功能.

为了在2.0之前完成这项工作,它需要您在框架上执行重要的重新架构,或者编写自己的运行程序来识别您自己的特殊属性.

  • 所以,现在2.0已经发布了,不需要为所有测试提供公共基类(或者调用一些常用代码)的解决方案是实现自己的XunitTestFramework子类,在构造函数中进行初始化和终结器,然后使用TestFrameworkAttribute标记程序集.终结器的替代方法,如果您对此感到不舒服,则将对象添加到执行清理的基类TestFramework的受保护DisposalTracker属性中. (5认同)

Rol*_*sen 9

要在程序集初始化上执行代码,则可以执行此操作(已通过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();
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

另请参见https://github.com/xunit/samples.xunit/tree/master/AssemblyFixtureExample

  • @TonyQ我添加了IDisposable,之后Dispose开始被调用(MyClassName:XunitTestFramework,IDisposable)。另外,如果有人不知道“MyAssemblyName”是您的测试项目的名称,我也不知道。 (8认同)
  • 我认为这可能应该是公认的答案,并且绝对是我最终使用的答案。不需要用属性装饰我的所有测试类。 (3认同)

Shi*_*mmy 5

我使用AssemblyFixture ( NuGet )。

它的作用是提供一个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  
  }
}
Run Code Online (Sandbox Code Playgroud)