2012-10-19 56 views
37

这个问题是关于单元测试框架xUnit.netxUnit.net:全局设置+拆解?

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

或者,如果我调用的xUnit编程,我也可以做到什么,我想用下面的代码:

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

谁能给我提供一个关于如何以声明或编程方式运行一些全局安装/拆卸代码提示?

+1

我猜这里是答案:http://stackoverflow.com/questions/12379949/xunit-resharper-how-to-run-setup-code-only-once –

回答

44

据我所知,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>接口来确保仅调用一次全局初始化/拆卸功能。对于这个版本,你不会从你的测试类扩展基类,但其实现的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) 
    { 
    } 
} 

导致TestsFixture构造函数只被运行一次 为每个被测试的类。因此它取决于你想要在两种方法之间进行选择。

+3

看来IUseFixture不再存在,已被IClassFixture取代。 – GaTechThomas

+1

尽管这样做有效,但我认为从Geir Sagberg的答案中的CollectionFixture更适合这种情况,因为它是专门为此目的而设计的。你也不必继承你的测试类,只需用'[Collection(“”)]属性 – MichelZ

+0

标记它们有没有什么办法可以做异步设置和拆卸? – Andrii

-3

应该有bootstrap选项可以在其他任何地方运行代码。至少有PHPUnit。你没有提到你使用的是什么框架。

我不知道在所有测试后运行代码的任何方式。但是你可以在一个首先运行测试的包装脚本中执行此操作,然后运行你需要的任何东西。

+0

问题标题和标签中都的xUnit他们。 – Codism

+1

@Codism:“xUnit”通常是指用于各种语言的单元测试套件系列。他们是一个家庭,因为他们都是在JUnit之后建模的,而且结构非常相似。请参阅http://en.wikipedia.org/wiki/XUnit,或者将鼠标悬停在您添加到问题中的“xunit”标签上。听起来你正在使用.NET测试框架“xUnit”,但具体的一个是相当新的和未知的。 – DXM

+1

@DXM:谢谢澄清xunit。我会更新我的问题。 – Codism

7

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

https://github.com/Fody/ModuleInit

这是一个NuGet包,当你安装它,它增加了一个新的文件名为ModuleInitializer.cs到项目中。在这里有一个静态方法,它在构建之后被编织到程序集中,并在程序集加载之后以及运行之前立即运行。

我使用它来解锁我购买的库的软件许可证。我总是忘记在每个测试中解锁许可证,甚至忘记从可以解锁它的基类中派生出测试。写这个库的明亮的火花,而不是告诉你它是许可证锁定引入了微妙的数字错误,导致测试失败或通过时,他们不应该。你永远不会知道你是否正确地解锁了图书馆。所以现在我的模块初始化看起来像

/// <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"); 
    } 
} 

并且放入此程序集的所有测试都将正确解锁它们的许可证。

+2

坚实的想法;不幸的是,它似乎还没有与DNX单元测试一起工作。 –

5

要在多个类之间共享SetUp/TearDown代码,可以使用xUnit的CollectionFixture

报价:

要使用集灯具,需要采取以下步骤:

  • 创建夹具类,并把启动代码的夹具类的构造函数。
  • 如果夹具类需要执行清理,请在夹具类上实现IDisposable,然后将清理代码放入Dispose()方法 中。
  • 创建集合定义类,使用[CollectionDefinition]属性对其进行装饰,给它一个唯一的名称,将 标识测试集合。
  • 将ICollectionFixture <>添加到集合定义类。
  • 使用提供给测试 集合定义类的[CollectionDefinition]属性的唯一名称,将[Collection]属性添加到将成为集合一部分的所有测试类。
  • 如果测试类需要访问灯具实例,请将其添加为构造函数参数,并自动提供。
15

我有点迟到了,但我一直在寻找相同的答案,而此时的xUnit文档是非常有帮助的关于如何实现类灯具和灯具收藏,让开发人员在班级或班级级别设置/拆卸各种功能。这与Geir Sagberg的回答一致,并提供了良好的骨架实现来说明它应该是什么样子。

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

收藏灯具 何时使用:当你想创建一个单一的测试环境和在几个测试类测试中共享,并使其在测试类所有的测试后清理完成了。

有时你会想要在多个测试类中共享一个夹具对象。用于类装置的数据库示例就是一个很好的例子:您可能想用一组测试数据初始化数据库,然后将测试数据保留在适当的位置以供多个测试类使用。您可以使用xUnit.net的集合夹具功能在多个测试类中的测试之间共享单个对象实例。

要使用集灯具,需要采取以下步骤:

创建夹具类,并把启动代码的夹具类的构造函数。 如果fixture类需要执行清理,请在fixture类上实现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在并行运行时运行测试的方式。有关更多信息,请参阅并行运行测试。

重要提示:夹具必须与使用它们的测试在同一个装配体中。

+0

“测试集合也可以使用IClassFixture <>进行修饰,xUnit.net将此视为好像测试集合中的每个单独测试类都使用了类装饰。”任何机会我可以得到这样的例子?我不太明白。 –

+0

@TannerFaulkner 类灯具是一种方法,有一个类级别的安装和拆卸,就像你与传统的.NET单元测试项目获得,当你有一个测试初始化​​方法: [TestInitialize] 公共无效初始化(){ –

+0

Errr ....: - /我在上面的评论中应该说[ClassInitialze]。 –