这个问题是关于单元测试框架xUnit.net的。
我需要在执行任何测试之前运行一些代码,也需要在所有测试完成后运行一些代码。我认为应该有某种属性或标记接口来指示全局初始化和终止代码,但找不到它们。
或者,如果我以编程方式调用xUnit,我也可以通过以下代码实现我想要的结果:
static void Main()
{
try
{
MyGlobalSetup();
RunAllTests(); // What goes into this method?
}
finally
{
MyGlobalTeardown();
}
}有没有人能给我一个关于如何以声明方式或编程方式运行一些全局设置/拆卸代码的提示?
发布于 2013-05-16 22:48:17
据我所知,xUnit没有全局初始化/拆卸扩展点。然而,创建一个很容易。只需创建一个实现IDisposable的基本测试类,并在构造函数中执行初始化,在IDisposable.Dispose方法中执行teardown。这看起来像这样:
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引用您的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)
{
}
}这将导致TestsFixture的构造函数对每个被测类只运行一次。因此,这取决于您到底想在这两种方法中选择哪种方法。
发布于 2017-02-02 01:22:17
我一直在寻找同样的答案,此时xUnit文档对于如何实现类固定装置和集合固定装置非常有帮助,这些固定装置为开发人员在类或类组级别提供了广泛的设置/拆卸功能。这与Geir Sagberg的答案是一致的,并给出了很好的框架实现来说明它应该是什么样子。
https://xunit.net/docs/shared-context
何时使用
集合装置:当您想要创建一个测试上下文并在多个测试类中的测试之间共享它,并在测试类中的所有测试完成后将其清除时。有时,您可能希望在多个测试类之间共享一个fixture对象。用于类fixture的数据库示例就是一个很好的示例:您可能希望使用一组测试数据初始化一个数据库,然后将该测试数据保留在适当的位置,以供多个测试类使用。您可以使用xUnit.net的集合装置特性在多个测试类中的测试之间共享单个对象实例。要使用集合fixture,需要执行以下步骤:创建fixture类,并将启动代码放入fixture类构造函数中。如果fixture类需要执行清理,请在fixture类上实现IDisposable,并将清理代码放入Dispose()方法中。创建集合定义类,使用CollectionDefinition属性对其进行修饰,为其提供一个惟一的名称来标识测试集合。将ICollectionFixture<>添加到集合定义类中。使用您为测试集合定义类的CollectionDefinition属性提供的唯一名称,将集合属性添加到将成为集合一部分的所有测试类。如果测试类需要访问fixture实例,则将其添加为构造函数参数,它将自动提供。下面是一个简单的例子:
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对待集合fixture的方式与对待类fixture的方式大致相同,不同之处在于集合fixture对象的生命周期更长:它是在对集合中的任何测试类运行任何测试之前创建的,并且在集合中的所有测试类完成运行之前不会被清除。测试集合也可以用IClassFixture<>进行装饰。xUnit.net将其视为测试集合中的每个单独的测试类都使用类fixture进行装饰。当并行运行测试集合时,测试集合还会影响xUnit.net运行测试的方式。有关更多信息,请参见并行运行测试。重要说明:夹具必须与使用它们的测试位于同一部件中。
发布于 2014-03-26 20:10:58
有一个简单易用的解决方案。使用Fody.ModuleInit插件
https://github.com/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");
}
}放入此程序集中的所有测试都将为它们正确解锁许可证。
https://stackoverflow.com/questions/12976319
复制相似问题