C# xUnit.net:全局设置+;拆卸?

C# xUnit.net:全局设置+;拆卸?,c#,.net,xunit.net,C#,.net,Xunit.net,这个问题是关于单元测试框架的 我需要在执行任何测试之前运行一些代码,在所有测试完成之后也需要运行一些代码。我认为应该有某种属性或标记接口来指示全局初始化和终止代码,但找不到它们 或者,如果以编程方式调用xUnit,还可以使用以下代码实现所需的功能: static void Main() { try { MyGlobalSetup(); RunAllTests(); // What goes into this method? }

这个问题是关于单元测试框架的

我需要在执行任何测试之前运行一些代码,在所有测试完成之后也需要运行一些代码。我认为应该有某种属性或标记接口来指示全局初始化和终止代码,但找不到它们

或者,如果以编程方式调用xUnit,还可以使用以下代码实现所需的功能:

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

有谁能给我一个关于如何以声明方式或编程方式运行一些全局设置/拆卸代码的提示吗?

据我所知,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
}
[CollectionDefinition("TestEngine")]
public class TestEngineInitializer: IDisposable, ICollectionFixture<TestEngineInitializer>
{
    public TestEngineInitializer()
    {
        MyOwnTestEngine.Init();
    }

    public void Dispose()
    {
        MyOwnTestEngine.Cleanup();
    }
}
[Collection("TestEngine")]
public class MyTests
{
     public MyTests(TestEngineInitializer initializer)
     {
     }
但是,每个调用都将执行基类设置和拆卸代码。这可能不是你想要的,因为它不是很有效。更优化的版本将使用
IClassFixture
接口来确保全局初始化/拆卸功能只调用一次。对于此版本,您不会从测试类扩展基类,而是实现
IClassFixture
接口,其中
t
引用您的fixture类:

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 DummyTests(TestsFixture data)
    {
    }
}
使用Xunit;
公共类测试固件:IDisposable
{
公共测试混合()
{
//在这里进行“全局”初始化;只调用一次。
}
公共空间处置()
{
//在此处执行“全局”拆卸;仅调用一次。
}
}
公共类DummyTests:IClassFixture
{
公共DummyTests(TestsFixture数据)
{
}
}
这将导致
TestsFixture
的构造函数只运行一次
对于每一个被测试的类。因此,这取决于您想要在这两种方法中选择什么。

有一个简单的解决方案。使用Fody.ModuleInit插件

它是一个nuget软件包,安装时会向项目中添加一个名为
ModuleInitializer.cs
的新文件。这里有一个静态方法,它在构建之后编织到程序集中,并在加载程序集之后和运行任何操作之前运行

我用它来解锁我购买的库的软件许可证。我总是忘记在每次测试中解锁许可证,甚至忘记从一个可以解锁许可证的基类派生测试。编写这个库的明亮火花并没有告诉您它是许可证锁定的,而是引入了微妙的数字错误,导致测试在不应该的时候失败或通过。您永远不会知道是否已正确解锁库。现在我的模块init看起来像

/// <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");
    }
}
//
///由模块单元使用。一旦加载程序集,Initialize方法中的所有代码都将立即运行。
/// 
公共静态类模块初始化器
{
/// 
///初始化模块。
/// 
公共静态void Initialize()
{
SomeLibrary.LicenseUtility.Unlock(“XXXX-XXXX-XXXX-XXXX-XXXX”);
}
}

并且放置在此程序集中的所有测试都将正确解锁许可证。

要在多个类之间共享安装/拆卸代码,可以使用xUnit的

引述:

要使用收集装置,需要执行以下步骤:

  • 创建fixture类,并将启动代码放入fixture类构造函数中
  • 如果fixture类需要执行清理,请在fixture类上实现IDisposable,并将清理代码放入Dispose()中 方法
  • 创建集合定义类,使用[CollectionDefinition]属性对其进行修饰,并为其指定一个唯一的名称,该名称将 确定测试集合
  • 将ICollectionFixture添加到集合定义类
  • 使用您为测试提供的唯一名称,将[Collection]属性添加到将成为集合一部分的所有测试类 集合定义类的[CollectionDefinition]属性
  • 如果测试类需要访问fixture实例,则将其添加为构造函数参数,并自动提供

我一直在寻找相同的答案,而此时的xUnit文档对于如何实现类固定装置和集合固定装置非常有帮助,它们为开发人员提供了类或类组级别的各种设置/拆卸功能。这与Geir Sagberg的答案一致,并给出了很好的框架实现来说明它应该是什么样子

收集装置 何时使用:当您希望创建单个测试上下文并在多个测试类中的测试之间共享它,并在测试类中的所有测试完成后将其清除时

有时,您会希望在多个测试类之间共享一个fixture对象。用于类fixture的数据库示例是一个很好的示例:您可能希望使用一组测试数据初始化数据库,然后将该测试数据保留在适当的位置以供多个测试类使用。您可以使用xUnit.net的collection fixture功能在多个测试类中的测试之间共享单个对象实例

要使用收集装置,需要执行以下步骤:

创建fixture类,并将启动代码放入fixture类构造函数中。 如果fixture类需要执行清理,请在fixture类上实现IDisposable,并将清理代码放入Dispose()方法中。 创建集合定义类,使用[CollectionDefinition]属性对其进行修饰,并为其指定一个唯一的名称来标识测试集合。 将ICollectionFixture添加到集合定义类。 将[Collection]属性添加到将成为集合一部分的所有测试类,
[Collection("TestEngine")]
public class MyTests
{
[Collection("TestEngine")]
public class MyTests
{
     public MyTests(TestEngineInitializer initializer)
     {
     }