NUnit 测试运行顺序

默认情况下,单元测试按字母顺序运行。有没有人知道如何设定死刑执行顺序?是否存在此属性?

97432 次浏览

您的单元测试应该能够独立运行并且独立运行。如果他们满足这个标准,那么顺序并不重要。

然而,在某些情况下,您会希望首先运行某些测试。一个典型的例子是在持续集成的情况下,一些测试比其他测试运行的时间更长。我们使用类别属性,这样我们就可以在使用数据库的测试之前运行使用模拟的测试。

也就是说,把这个放在快速测试的开始

[Category("QuickTests")]

如果您有依赖于某些环境条件的测试,请考虑 TestFixtureSetUpTestFixtureTearDown属性,它们允许您在测试之前和之后标记要执行的方法。

您不应该依赖于测试框架为执行挑选测试的顺序。测试应该是独立的。在这方面,他们不应该依靠其他一些测试为他们设置舞台或清理后,他们。不管测试的执行顺序如何,它们也应该产生相同的结果(对于 SUT 的给定快照)

我谷歌了一下。像往常一样,一些人采取了卑鄙的手段(而不是解决潜在的可测试性/设计问题

  • 按字母顺序命名测试,使测试按照它们“需要”执行的顺序出现。然而,NUnit 可能会选择在稍后的版本中更改这种行为,然后您的测试将被清除。最好将当前的 NUnit 二进制文件检查到源代码管理。
  • 在他们的 MS 测试框架中有一些被称为“有序测试”的东西。我没有浪费任何时间阅读,但它似乎是针对同一个受众

参见: 好测试的特点

希望测试以特定的顺序运行并不意味着测试是相互依赖的——我目前正在做一个 TDD 项目,作为一个优秀的 TDD 工程师,我嘲笑/阻碍了所有的事情,但是如果我可以指定测试结果的 展示顺序——按主题而不是按字母顺序——它会使测试更具可读性。到目前为止,我唯一能想到的就是在类、名称空间和方法的类前面加上一个 _ b _ c _。(不太好)我认为一个[ TestOrderAttribute ]属性会很好——不是严格遵循框架,而是一个提示,这样我们就可以实现这一点

我只是想指出,虽然大多数响应者认为这些是单元测试,但问题并没有指明它们是单元测试。

NUnit 是一个很好的工具,可以用于各种测试情况。我可以理解为什么要控制测试顺序。

在这些情况下,我不得不将一个运行顺序合并到测试名称中。如果能够使用属性指定运行顺序就太好了。

通常单元测试应该是独立的,但是如果必须的话,你可以用字母顺序 x 来命名你的方法:

[Test]
public void Add_Users(){}


[Test]
public void Add_UsersB(){}


[Test]
public void Process_Users(){}

或者你可以..。

        private void Add_Users(){}


private void Add_UsersB(){}


[Test]
public void Process_Users()
{
Add_Users();
Add_UsersB();
// more code
}

我使用的是用 C # 编写的 Selenium WebDriver 端到端 UI 测试用例,它们使用 NUnit 框架运行。(不包括单位案件本身)

这些 UI 测试当然取决于执行的顺序,因为其他测试需要添加一些数据作为前提条件。(在每个测试中做这些步骤是不可行的)

现在,在添加了第10个测试用例之后,我看到 NUnit 希望按以下顺序运行: 测试 _ 1 测试 _ 10 测试 _ 2 测试 _ 3 ..

所以我想我现在必须按字母顺序排列测试用例的名称,但是将这个控制执行顺序的小特性添加到 NUnit 中会很好。

我正在一个相当复杂的网站上使用 Selenium 进行测试,整套测试可以运行半个多小时,我还没有接近覆盖整个应用程序。如果我必须确保所有以前的表格都为每个测试正确填写,这将为整个测试增加大量的时间,而不仅仅是少量的时间。如果运行测试的开销太大,人们就不会像应该的那样频繁地运行测试。

因此,我将它们按顺序排列,并依赖于以前的测试,使文本框和这样的测试完成。我使用 Assert。当前置条件无效时忽略() ,但是我需要它们按顺序运行。

不管测试是否依赖于顺序... 我们中的一些人只是想控制一切,以一种有序的方式。

单元测试通常是按照复杂性的顺序创建的。那么,为什么它们不能按照复杂性的顺序运行,或者按照创建它们的顺序运行呢?

就个人而言,我喜欢看到测试按照我创建它们的顺序运行。在 TDD 中,每个连续的测试自然会更加复杂,需要花费更多的时间来运行。我宁愿先看到较简单的测试失败,因为它将成为失败原因的更好指示器。

但是,我也可以看到以随机顺序运行它们的好处,特别是如果您希望测试您的测试与其他测试没有任何依赖关系。如何添加一个选项来测试跑步者“运行测试随机直到停止”?

我知道这是一个相对较老的帖子,但这里有另一种方法来保持您的测试顺序,而不使测试名称尴尬。通过使用 TestCaseSource 属性并让您传入的对象有一个委托(Action) ,您完全不仅可以控制顺序,而且还可以为测试命名。

这是可行的,因为根据文档,从测试源返回的集合中的项将始终按列出的顺序执行。

这是我明天演讲的样带:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;


namespace NUnitTest
{
public class TestStructure
{
public Action Test;
}


class Int
{
public int I;
}


[TestFixture]
public class ControllingTestOrder
{
private static readonly Int MyInt= new Int();


[TestFixtureSetUp]
public void SetUp()
{
MyInt.I = 0;
}


[TestCaseSource(sourceName: "TestSource")]
public void MyTest(TestStructure test)
{
test.Test();
}


public IEnumerable<TestCaseData> TestSource
{
get
{
yield return new TestCaseData(
new TestStructure
{
Test = () =>
{
Console.WriteLine("This is test one");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(1));
}
}).SetName(@"Test One");
yield return new TestCaseData(
new TestStructure
{
Test = () =>
{
Console.WriteLine("This is test two");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(2));
}
}).SetName(@"Test Two");
yield return new TestCaseData(
new TestStructure
{
Test = () =>
{
Console.WriteLine("This is test three");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(3));
}
}).SetName(@"Test Three");
}
}
}
}

我真的很喜欢之前的答案。

我稍微修改了一下,以便能够使用一个属性来设置订单范围:

namespace SmiMobile.Web.Selenium.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework;


public class OrderedTestAttribute : Attribute
{
public int Order { get; set; }




public OrderedTestAttribute(int order)
{
Order = order;
}
}


public class TestStructure
{
public Action Test;
}


class Int
{
public int I;
}


[TestFixture]
public class ControllingTestOrder
{
private static readonly Int MyInt = new Int();


[TestFixtureSetUp]
public void SetUp()
{
MyInt.I = 0;
}


[OrderedTest(0)]
public void Test0()
{
Console.WriteLine("This is test zero");
Assert.That(MyInt.I, Is.EqualTo(0));
}


[OrderedTest(2)]
public void ATest0()
{
Console.WriteLine("This is test two");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(2));
}




[OrderedTest(1)]
public void BTest0()
{
Console.WriteLine("This is test one");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(1));
}


[OrderedTest(3)]
public void AAA()
{
Console.WriteLine("This is test three");
MyInt.I++; Assert.That(MyInt.I, Is.EqualTo(3));
}




[TestCaseSource(sourceName: "TestSource")]
public void MyTest(TestStructure test)
{
test.Test();
}


public IEnumerable<TestCaseData> TestSource
{
get
{
var assembly =Assembly.GetExecutingAssembly();
Dictionary<int, List<MethodInfo>> methods = assembly
.GetTypes()
.SelectMany(x => x.GetMethods())
.Where(y => y.GetCustomAttributes().OfType<OrderedTestAttribute>().Any())
.GroupBy(z => z.GetCustomAttribute<OrderedTestAttribute>().Order)
.ToDictionary(gdc => gdc.Key, gdc => gdc.ToList());


foreach (var order in methods.Keys.OrderBy(x => x))
{
foreach (var methodInfo in methods[order])
{
MethodInfo info = methodInfo;
yield return new TestCaseData(
new TestStructure
{
Test = () =>
{
object classInstance = Activator.CreateInstance(info.DeclaringType, null);
info.Invoke(classInstance, null);
}
}).SetName(methodInfo.Name);
}
}


}
}
}
}

这个问题现在已经很老了,但是对于那些可能通过搜索找到这个问题的人来说,我从 user3275462和 PvtVandals/Rico 那里得到了非常好的答案,并将它们和我自己的一些更新一起添加到了 GitHub 存储库中。我还创建了 相关的博客文章与一些额外的信息,你可以看看更多的信息。

希望这对你们有所帮助。此外,我经常喜欢使用 Category 属性来区分集成测试或其他端到端测试与实际的单元测试。其他人指出,单元测试不应该有顺序依赖关系,但是其他测试类型通常会有顺序依赖关系,所以这提供了一种很好的方法,可以只运行您想要的测试类别,并对这些端到端测试进行排序。

NUnit 3.2.0添加了一个 OrderAttribute,参见:

Https://github.com/nunit/docs/wiki/order-attribute

例如:

public class MyFixture
{
[Test, Order(1)]
public void TestA() { ... }




[Test, Order(2)]
public void TestB() { ... }


[Test]
public void TestC() { ... }
}

我很惊讶 NUnit 社区没有提出任何东西,所以我自己创建了这样的东西。

我目前的 开发一个开源的图书馆,允许您订购您的测试与 NUnit。您可以订购测试夹具,也可以订购“订购测试规范”。

该图书馆具有以下特点:

  • 构建复杂的测试排序层次结构
  • 如果顺序测试失败,则跳过后续测试
  • 按照依赖关系而不是整数顺序来排序测试方法
  • 支持与无序测试并行使用。无序测试首先执行。

这个库的灵感实际上来自于 MSTest 如何使用 .orderedtest文件进行测试排序。

[OrderedTestFixture]
public sealed class MyOrderedTestFixture : TestOrderingSpecification {
protected override void DefineTestOrdering() {
TestFixture<Fixture1>();


OrderedTestSpecification<MyOtherOrderedTestFixture>();


TestFixture<Fixture2>();
TestFixture<Fixture3>();
}


protected override bool ContinueOnError => false; // Or true, if you want to continue even if a child test fails
}

有很好的理由使用测试订购机制。我自己的大多数测试都使用了很好的实践,比如 setup/teardown。另一些则需要大量的数据设置,然后可以用来测试一系列特性。到目前为止,我一直使用大型测试来处理这些(SeleniumWebDriver)集成测试。然而,我认为上面建议的关于 https://github.com/nunit/docs/wiki/Order-Attribute的帖子有很多优点。下面是一个例子,说明为什么订购会非常有价值:

  • 使用 Selenium WebDriver 运行测试以下载报告
  • 报告的状态(无论是否可下载)为 10分钟缓存
  • 这意味着,在每次测试之前,我需要重置报告状态,然后等到 10分钟状态被确认为已经更改,然后验证报告下载是否正确。
  • 由于报告的复杂性,无法通过模拟或测试框架内的任何其他机制以实际/及时的方式生成报告。

这10分钟的等待时间减慢了测试套件的速度。当您在多个测试中将类似的缓存延迟相乘时,会消耗大量时间。排序测试可以允许数据设置在测试套件开始时作为“测试”完成,测试依赖于缓存,在测试运行结束时执行测试。

如果使用 [TestCase],参数 TestName为测试提供一个名称。

如果未指定,则根据方法名和提供的参数生成名称。

您可以控制测试执行的顺序,如下所示:

[Test]
[TestCase("value1", TestName = "ExpressionTest_1")]
[TestCase("value2", TestName = "ExpressionTest_2")]
[TestCase("value3", TestName = "ExpressionTest_3")]
public void ExpressionTest(string  v)
{
//do your stuff
}

在这里,我使用了带有数字的方法名 "ExpressionTest"后缀。

您可以使用按字母顺序排列的任何名称 请参阅 < a href = “ https://github.com/nunit/docs/wiki/TestCase-Attribute”rel = “ nofollow noReferrer”> TestCase Attribute

在使用 TestCaseSource的情况下,关键是使用 override string ToString方法,它是如何工作的:

假设您有 TestCase 类

public class TestCase
{
public string Name { get; set; }
public int Input { get; set; }
public int Expected { get; set; }
}

以及一个 TestCase 列表:

private static IEnumerable<TestCase> TestSource()
{
return new List<TestCase>
{
new TestCase()
{
Name = "Test 1",
Input = 2,
Expected = 4
},
new TestCase()
{
Name = "Test 2",
Input = 4,
Expected = 16
},
new TestCase()
{
Name = "Test 3",
Input = 10,
Expected = 100
}
};
}

现在让我们将它与 Test 方法一起使用,看看会发生什么:

[TestCaseSource(nameof(TestSource))]
public void MethodXTest(TestCase testCase)
{
var x = Power(testCase.Input);
x.ShouldBe(testCase.Expected);
}

这将不按顺序进行测试,输出将如下所示:

enter image description here

所以如果我们把 override string ToString加到我们的类中,比如:

public class TestCase
{
public string Name { get; set; }
public int Input { get; set; }
public int Expected { get; set; }


public override string ToString()
{
return Name;
}
}

结果会改变,我们会得到测试的顺序和名称,比如:

enter image description here

注:

  1. 这只是一个例子来说明如何在测试中获得名称和顺序,顺序是按数字/字母顺序进行的,所以如果你有超过10个测试,我建议做测试01,测试02..。测试10,测试11等等,因为如果你做了测试1,并且在某个时刻测试10,那么顺序将是测试1,测试10,测试2。.等等。
  2. 输入和 Expect 可以是任何类型、字符串、对象或自定义类。
  3. 除了顺序之外,这里的好处是您可以看到更重要的测试名称。