运行长时间运行的NUnit/xUnit测试,以便它们不会阻塞其他测试

运行长时间运行的NUnit/xUnit测试,以便它们不会阻塞其他测试,nunit,xunit,Nunit,Xunit,我正在运行一组集成测试,虽然大多数测试都在合理的时间内完成,但有两个测试正在等待特定条件(金融市场条件要精确),它们可以持续2-3个小时。因此,理想情况下,我希望实现两件事: 在其他测试完成后开始这两个测试 并行运行它们 在NUnit/XUnit(或其他测试运行程序)中是否有实现这一点的方法 并行测试运行取决于测试运行程序的参数,如果您使用的是xUnit控制台测试运行程序,则存在-Parallel参数或MSBuild选项,请参阅:。但是在任何情况下,您都必须在单独的测试类上拆分长时间运行的测试

我正在运行一组集成测试,虽然大多数测试都在合理的时间内完成,但有两个测试正在等待特定条件(金融市场条件要精确),它们可以持续2-3个小时。因此,理想情况下,我希望实现两件事:

  • 在其他测试完成后开始这两个测试
  • 并行运行它们
  • 在NUnit/XUnit(或其他测试运行程序)中是否有实现这一点的方法

  • 并行测试运行取决于测试运行程序的参数,如果您使用的是xUnit控制台测试运行程序,则存在-Parallel参数或MSBuild选项,请参阅:。但是在任何情况下,您都必须在单独的测试类上拆分长时间运行的测试

  • 很难保证测试运行的顺序,您可以使用TestCollection(然而,根据按顺序运行的quide collection)。您可以重命名长时间运行的测试,将它们放在列表的末尾,即TestClass2将在TestClass1之后执行。您还可以使用category属性参数来分隔测试,并通过dotnet test中的两个命令运行它们--filter=TestCategory=LongTests(一个用于long,另一个用于其他),请参阅

  • 在其他测试完成后开始这两个测试

    您可以将这两个测试保留在一个单独的nunit测试项目中,从而允许您单独运行所有其他测试

    对于并行运行测试,本博客有一篇很好的文章:

    • 使用Parallelizable属性标记测试夹具,并将并行范围设置为ParallelScope.All

    • 创建一个名为TestScope的私有类并实现IDisposable

    • 将所有启动和清理逻辑分别放在TestScope构造函数和.Dispose()方法中

    • 将测试代码包装在using(var scope=newtestscope){…}块中

    [TestFixture]
    [可并行化(ParallelScope.All)]
    公共类MyClassTests{
    [测试]
    公共无效MyParallelTest(){
    使用(var scope=newtestscope()){
    scope.Sut.DoSomething();
    scope.Repository.Received(1.Save();
    }
    }
    私有密封类TestScope:IDisposable{
    公共IRepository存储库{get;}
    公共MyClass Sut{get;}
    公共测试范围(){
    Repository=replacement.For();
    Sut=新的MyClass(存储库);
    }
    公共空间处置(){
    //清理代码在这里
    存储库?.Dispose()
    }
    }
    }
    
    您应该采取预防措施,以确保并行运行时,测试不会相互干扰

    如该条所述:

    如何安全地并行运行测试

    允许测试并行运行 在没有他们相互干扰的情况下,我一直在应用 下面是一段时间的模式:

    • 创建实现IDisposable的嵌套私有TestScope类
    • 将进入安装方法的所有初始化或启动代码 进入TestScope类的构造函数
    • 任何清理或 将进入拆卸方法的拆卸代码将进入 Dispose方法所有测试都在处理 TestScope的创建和处置
    [TestFixture]
    [可并行化(ParallelScope.All)]
    公共类MyClassTests{
    [测试]
    公共无效MyParallelTest(){
    使用(var scope=newtestscope()){
    scope.Sut.DoSomething();
    scope.Repository.Received(1.Save();
    }
    }
    私有密封类TestScope:IDisposable{
    公共IRepository存储库{get;}
    公共MyClass Sut{get;}
    公共测试范围(){
    Repository=replacement.For();
    Sut=新的MyClass(存储库);
    }
    公共空间处置(){
    //清理代码在这里
    存储库?.Dispose()
    }
    }
    }
    

    这篇文章提供了更有价值的建议。我建议您阅读并感谢作者。

    您可以在不同的过程中运行测试
    [TestFixture]
    [Parallelizable(ParallelScope.All)]
    public class MyClassTests {
    
        [Test]
        public void MyParallelTest() {
            using(var scope = new TestScope()) {
                scope.Sut.DoSomething();
                scope.Repository.Received(1).Save();
            }
        }
    
        private sealed class TestScope : IDisposable {
            public IRepository Repository{get;}
            public MyClass Sut {get;}
            public TestScope() {
                Repository = Substitute.For<IRepository>();
                Sut = new MyClass(Repository);
            }
    
            public void Dispose() {
                //clean-up code goes here
                Repository?.Dispose()
            }
        }
    }
    
    [TestFixture]
    [Parallelizable(ParallelScope.All)]
    public class MyClassTests {
    
        [Test]
        public void MyParallelTest() {
            using(var scope = new TestScope()) {
                scope.Sut.DoSomething();
                scope.Repository.Received(1).Save();
            }
        }
    
        private sealed class TestScope : IDisposable {
            public IRepository Repository{get;}
            public MyClass Sut {get;}
            public TestScope() {
                Repository = Substitute.For<IRepository>();
                Sut = new MyClass(Repository);
            }
    
            public void Dispose() {
                //clean-up code goes here
                Repository?.Dispose()
            }
        }
    }