NUnit测试运行顺序


110

默认情况下,nunit测试按字母顺序运行。有人知道任何设置执行顺序的方法吗?是否存在属性?


7
你为什么想做这个?听起来您对运行顺序有依赖性,这是一件坏事。您需要重新考虑为什么要这样做。单元测试应该独立运行,并且完全独立于其他测试。听起来您正在为测试气味Erratic Tests创建候选对象。
RichardOD

这看起来像重复的,但是您可以在这里
Johnno Nolan

12
@RichardOD-应该!=必须。这实际上不是应该的事情,因为实际上几乎所有集成测试都是按顺序运行的-问问质量检查小组,他们是否将测试顺序随机化。
tymtam

4
我目前有一些测试,尽管顺序无关紧要,但顺序似乎很重要。就个人而言,我想要一种随机分配测试顺序的方法,以帮助我确保测试不会以某种方式依赖于测试。当然,我真正想要的是一个测试运行程序,它将以随机顺序运行我的所有测试,直到发现问题,或者我说停止为止。如果我整夜奔跑,而早晨一切仍然绿意盎然,那么我可以确信已经消除了最后的意外副作用。
梅尔

1
如果问题被更新以指明我们在这里谈论集成测试,那么这个问题将变得更加相关。我们都知道有关单元测试的规则,至少如果您已阅读XUnit测试模式并遵循Bob叔叔等。你做..但框架,如NUnit的也是越来越集成测试快速启动和运行,以及真正有用的..你肯定不希望这些是随机的,昂贵的数据库安装涉及特别是当..
nrjohnstone

Answers:


51

您的单元测试应该能够独立运行并且独立运行。如果它们满足此条件,则顺序无关紧要。

但是,在某些情况下,您将需要首先运行某些测试。一个典型的例子是在持续集成情况下,某些测试的运行时间比其他测试更长。我们使用category属性,以便我们可以在使用数据库的测试之前运行使用模拟的测试。

即把它放在您的快速测试的开始

[Category("QuickTests")]

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


2
@Chris,我倾向于不使用这些属性,这是一个有趣的博客post- jamesnewkirk.typepad.com/posts/2007/09/why-you-should-.html。不过,关于类别测试的要点。
RichardOD

29
依赖顺序测试的另一个示例是当您想使用nunit框架运行集成测试时...
Byron Ross

1
@ByronRoss:这样做时,我倾向于使测试更大,并尽可能依靠现有的单元测试,这样我可以编写更少的测试。然后,每个完整运行可能会分别失败。我还尝试设计数据,使其可以与现有数据分开存在,而不是依赖于现有数据。
Merlyn Morgan-Graham

1
如果您不是按随机顺序运行测试,那么如何在顺序混乱时验证它们是否确实有效?您的意思类似于说“如果您的软件没有错误,则不需要测试”。
jforberg 2014年

@jforberg:如果您发生的故障是随机发生的,那么您在修复故障后怎么说呢?
NeedHack 2014年

175

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

nUnit是一个很棒的工具,可以用于各种测试情况。我可以看到想要控制测试订单的适当原因。

在这种情况下,我不得不求助于将运行顺序合并到测试名称中。能够使用属性指定运行顺序会很棒。


您是说打了电话,例如,001_first_test 002_second_test等等?
ashes999 '02

是的,这是正确的,但我通常使用的模式,可以让我轻松地插入测试如果必要的话,也许010_first_test,020_second_test等
莱斯

83
谢谢您唯一明智的回答。这是一个特定的问题,但人们却以某种方式质疑模糊的学问答案。是的,我们都知道单元测试应该是什么,但这不是问题。
Egor Pavlikhin 2012年

1
你应该依赖于运行顺序是字母上。许多测试运行程序可以同时在多个线程或进程上运行测试,而不必按字母顺序运行。例如,NCrunch会根据您更改的代码(受影响的测试),然后根据上次失败的测试,然后是快速运行还是缓慢运行来对测试进行优先级排序。如果要定义顺序,只需为这些测试创建一个元运行器,然后将其从常规运行中排除。
亚伯(Abel)

3
指定顺序的另一个很好的理由是,在继续测试套件的其余部分之前,首先要发现某些问题。换句话说,如果创建用户的测试失败,那么在找到此结果之前,我不需要运行其他所有内容。在我的其他测试中,我可能会嘲笑用户,但是对我来说重要的是,这是第一个失败的测试-特别是在测试套件很大时。
Bron Davies

124

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() { ... }
}

5
Thanx,
直截了当

1
然后将它们设为一个整数,而不是十进制,因此如果必须插入测试,则所有测试都必须重新编号。
Epitka '16

如果您想以这种方式介绍中间项目-只需输入更多内容,就总是可以从数百万之类的非常大的数字开始。Imo应该有一种比使用数字优先级(例如方法依赖树)更简单的方法来排序事物,但是每种方法都有其自身的缺点/优势。
拉兹万·弗拉维斯·熊猫

9
好的答案,但是请注意,文档说明测试不要等待先前的测试完成。
ROX

有趣的是,根据我的经验,如果我们分别添加Test和Order属性,则不能保证测试的顺序。
AT

22

希望测试按特定顺序运行并不意味着测试相互依赖-我目前正在开发一个TDD项目,作为一个好的TDDer,我已经对所有内容进行了模拟/存根处理,但这会使如果我可以指定测试结果的显示顺序(主题顺序而不是字母顺序),则更具可读性。到目前为止,我唯一能想到的就是在类,名称空间和方法的类之前添加a_ b_ c_。(不好)我认为[TestOrderAttribute]属性会很好-并非严格遵循框架,而是提示,以便我们可以实现


10

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

通常按复杂性顺序创建单元测试。那么,为什么不也应该按照复杂性或创建顺序来运行它们呢?

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

但是,我还可以看到以随机顺序运行它们的好处,特别是如果您要测试自己的测试对其他测试没有任何依赖性时,尤其如此。如何在“随机运行测试直到停止”中向测试跑步者添加选项?


9

我正在一个相当复杂的网站上使用Selenium进行测试,并且整个测试套件可以运行超过半小时,而我还不能涵盖整个应用程序。如果必须确保为每个测试正确填写所有先前的表格,那么这将为整个测试增加大量时间,而不仅仅是少量时间。如果运行测试有太多开销,那么人们将不会经常运行它们。

因此,我将它们整理好并依靠以前的测试来完成文本框等。当前提条件无效时,我将使用Assert.Ignore(),但需要按顺序运行它们。


1
完全。我在同一条船上。
史密斯卧铺

我也是!正是我为什么要提出这个问题!
Niklas Wulff

@NiklasRingdahl(如果您将Visual Studio与nunit一起使用),请转储nunit并使用MS测试。那么您可以利用Visual Studio的orderedtest文件按照您希望它们执行的顺序排列测试用例
Rahul Lodha 2015年

@RahulLodha谢谢!我会调查的。
Niklas Wulff

9

我真的很喜欢上一个答案。

我做了一些更改,以便能够使用属性来设置订单范围:

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

            }
        }
    }
}

我认为这种方法很棒。请注意,尽管反射代码将拉动具有该属性的所有方法,所以如果您尝试运行特定的测试治具,您可能会惊讶地发现它的运行超出了您的想象。如果这不是所需的行为,则可以轻松地修改LINQ查询。有关更多信息,请参见我的答案中的链接。
克里斯皮

OrderedTest在NUnit的3不再支持
康拉德

7

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

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

这是我明天要进行的演示的演示:

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

我将这种模式用于并行集成测试,这将非常耗时,无法线性运行。所有测试都在一种类型上,所以我使用Action <T in>,并且每个Action都有一个键,该键说明“ ShouldBeFoo”的作用。这样,测试的行为将在测试名称中可见,并且可以过滤TestCaseSource,以便将不同类型的测试组合在一起。具有讽刺意味的是,我不在乎执行顺序,但同意它会起作用。
Novaterata 2014年

使用TestCaseSource的方式来运行有序的测试是神来之笔。做得好。我将这种方法与下面的方法一起使用,并添加了一些其他修改以使其更易于使用。请参阅我的答案中的链接以获取更多信息,但是基本的想法来自于这个好答案!
克里斯比(Chrispy)2014年

可悲的是,对于NUnit 3,的来源TestCaseSource必须是静态的,从而无法使用模式。笨蛋
康拉德

@康拉德 我看不出使该方法静态或不静态有什么区别。测试仍然以两种方式返回。
戴夫·布什

这不是TestCaseSource必须是静态的方法-NUnit 3中的源(变量或属性)必须是静态对象,否则测试将不会执行。而且您不能在静态对象内创建动态对象。这就是为什么它在第3节中不起作用的原因
Conrad

5

我正在使用C#编写的Selenium WebDriver端到端UI测试用例,这些用NUnit框架运行。(不是单位案例)

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

现在,在添加第10个测试用例之后,我看到NUnit要按以下顺序运行:Test_1 Test_10 Test_2 Test_3 ..

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


9
不同意Arran:UI测试本质上是一系列小步骤。每个步骤都应该是一个测试(原因-如果失败,我需要知道哪个步骤)。顺序可以是独立的,但在顺序内,顺序很重要,因此必须停止失败。
Zasz 2012年

3

通常,单元测试应该是独立的,但是如果必须,则可以按字母顺序命名方法,例如:

[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
        }

2
除了现在,名称必须按字母顺序排列,这是一个糟糕的解决方案。:(

@ user166390-并非如此。它的工作原理取决于NUnit记录的行为。
tymtam

for1对我来说已经足够好了,如果您使用a_ b_ t1_t2_而不是依靠容易错过的结尾字符开始测试,则容易
得多

3

有充分的理由使用测试订购机制。我自己的大多数测试都使用良好的惯例,例如设置/拆卸。其他一些则需要大量的数据设置,然后可以将其用于测试一系列功能。到目前为止,我已经使用大型测试来处理这些(Selenium Webdriver)集成测试。但是,我认为上面建议的https://github.com/nunit/docs/wiki/Order-Attribute上的帖子 有很多优点。这是为什么订购非常有价值的示例:

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

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


2

这个问题现在真的很老了,但是对于那些可能通过搜索找到答案的人来说,我从user3275​​462和PvtVandals / Rico中获得了很好的答案,并将它们以及一些我自己的更新添加到GitHub存储库中。我还创建了一个关联的博客文章,其中包含一些其他信息,您可以查看更多信息。

希望这对大家有帮助。另外,我经常喜欢使用Category属性将我的集成测试或其他端到端测试与实际的单元测试区分开。其他人指出,单元测试不应具有顺序依赖性,但是其他测试类型通常具有顺序依赖性,因此这提供了一种很好的方式,使其仅运行所需的测试类别并对这些端到端测试进行排序。


你可以帮我一个问题,我也有,这里是链接:stackoverflow.com/questions/31281395/...
摩根索伦

1

我很惊讶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
}

1

如果使用[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"带数字的方法名称后缀。

您可以使用按字母顺序排列的任何名称,请参见TestCase属性


0

您不应该依赖于测试框架选择执行测试的顺序。测试应该是独立且独立的。因为它们不应该依赖于其他测试来设置它们的工作台或清理它们。无论测试的执行顺序如何(对于SUT的给定快照),它们也应产生相同的结果。

我做了一些谷歌搜索。像往常一样,有些人采取了偷偷摸摸的手段(而不是解决潜在的可测试性/设计问题)

  • 以字母顺序命名测试,以使测试以“需要”执行的顺序出现。但是,NUnit可能会选择在以后的版本中更改此行为,然后将进行测试。最好将当前的NUnit二进制文件检入Source Control。
  • VS(IMHO使用其“敏捷工具”鼓励错误行为)在其MS测试框架中被称为“有序测试”。我没有浪费时间阅读,但似乎是针对相同的受众

另请参见:良好测试的特征


在某些情况下,您希望在长期运行的测试之前执行运行速度更快的测试,尤其是在集成和验收测试中。例如,在博客应用程序中:您首先测试登录名,因为如果该功能不起作用,则发布也将不起作用,因此执行该测试没有任何意义(您可以手动停止跑步者)。但是,如果您尝试继续运行测试,则将花费更多时间。
Marcel Valdez Orozco

@MarcelValdezOrozco-您可以通过通过不同的物理dll或使用标签/类别对测试进行分区来实现该目标。您可以创建构建脚本以按顺序运行dll /类别。通常,允许对测试进行排序通常会导致耦合测试对相邻测试产生依赖性(当然,随着时间的推移)。AFAIR在NUnit的的下一个大的版本,它会支持不同的排序的测试,例如,随机等
Gishu

2
进一步划分相同类型的测试(例如验收测试)是没有意义的,将它们分成另一个DLL不必要地增加了各处的复杂性:源代码,构建脚本,测试脚本,项目结构等。只是为了对它们进行简单的重新排序测试。
马塞尔·瓦尔德兹·奥罗斯科

发出(所以基本上所有的模拟库)都是顺序很重要的原因。您不能卸载您的应用程序域,并且nunit运行程序(如果在程序集中运行所有测试)会将所有测试的域保留在至少“固定”状态。如果一个测试创建了一个类型来测试某项,而另一个测试由于顺序而与创建的类型冲突,那不是错误的测试。它们在逻辑上是隔离的,只是nUnit没有在每个“测试”本身之间提供适当的隔离。
凯利·艾尔顿

6
这是一个令人鼓舞的答案,它使它很有趣,因为它实际上仅适用于单元测试,而完全忽略了实用性。
tymtam

0

如果使用TestCaseSource键是to override string ToString方法,那么它如何工作:

假设您有TestCase类

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

以及TestCases的列表:

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

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

在此处输入图片说明

因此,如果我们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;
    }
}

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

在此处输入图片说明

注意:

  1. 这只是说明如何在测试中获取名称和顺序的示例,该顺序以数字/字母顺序进行,因此如果您有十个以上的测试,我建议您进行测试01,测试02...。测试10,测试11等,因为如果您进行了测试1,并在某个时候进行了测试10,则订单将依次为测试1,测试10,测试2等。
  2. 输入和Expected可以是任何类型,字符串,对象或自定义类。
  3. 除了顺序之外,这里的好处是您看到了更重要的测试名称。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.