NUnit测试运行顺序

时间:2009-07-03 09:51:27

标签: .net attributes tdd nunit

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

16 个答案:

答案 0 :(得分:165)

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

nUnit是一款出色的工具,可用于各种测试环境。我可以看到想要控制测试订单的合适理由。

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

答案 1 :(得分:99)

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

答案 2 :(得分:48)

您的单元测试应该能够独立运行并且独立运行。如果他们满足这个标准,则顺序无关紧要。

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

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

[Category("QuickTests")]

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

答案 3 :(得分:21)

希望测试按特定顺序运行并不意味着测试依赖于彼此 - 我现在正在开发TDD项目,而且我是一个很好的TDDer我嘲笑/删除了所有内容,但是如果我可以指定测试结果显示的顺序 - 它将使其更具可读性 - 主题而不是按字母顺序排列。到目前为止,我唯一能想到的是将a_ b_ c_添加到类,命名空间和方法的类中。 (不太好)我认为[TestOrderAttribute]属性会很好 - 不是严格遵循框架,而是提示我们可以实现这个

答案 4 :(得分:10)

无论测试是否依赖于订单......我们中的一些人只是想以有序的方式控制一切。

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

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

但是,我也可以看到以随机顺序运行它们的好处,特别是如果你想测试你的测试没有任何依赖于其他测试。如何将测试跑步者的选项添加到“随机运行测试直到停止”?

答案 5 :(得分:9)

我正在一个相当复杂的网站上测试Selenium,整套测试可以运行半个多小时,而且我还没有覆盖整个应用程序。如果我必须确保每次测试都正确填写所有以前的表格,这会给整个测试增加大量时间,而不仅仅是少量时间。如果运行测试的开销太大,人们就不会经常运行它们。

所以,我把它们按顺序排列,并依赖于以前的测试来完成文本框。当前置条件无效时我使用Assert.Ignore(),但我需要让它们按顺序运行。

答案 6 :(得分: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);
                    }
                }

            }
        }
    }
}

答案 7 :(得分:7)

我知道这是一个相对较旧的帖子,但这是另一种方法来保持测试顺序,而不会让测试名称变得尴尬。通过使用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");
            }
        }
    }
}

答案 8 :(得分:5)

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

这些UI测试肯定取决于执行顺序,因为其他测试需要添加一些数据作为前提条件。 (在每次测试中都不可行)

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

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

答案 9 :(得分:3)

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

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

答案 10 :(得分:3)

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

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

这10分钟的等待时间会减慢测试套件的速度。当您在多个测试中乘以类似的缓存延迟时,会耗费大量时间。订购测试可以允许数据设置作为&#34;测试&#34;就在测试套件的开头,测试依赖于缓存,以便在测试运行结束时执行。

答案 11 :(得分:2)

这个问题现在已经很老了,但是对于那些可能通过搜索获得这个问题的人来说,我从user3275​​462和PvtVandals / Rico那里得到了很好的答案,并将它们与我自己的一些更新一起添加到GitHub repository。我还创建了an associated blog post,其中包含一些您可以查看的其他信息以获取更多信息。

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

答案 12 :(得分:1)

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

我目前developing an open-source library允许您使用NUnit订购测试。您可以订购测试夹具并订购&#34;订购的测试规格&#34;一样。

该库提供以下功能:

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

该库实际上受到了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
}

答案 13 :(得分:0)

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

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

  • 以按字母顺序排列的方式命名测试,以便测试按照“需要”执行的顺序显示。但是,NUnit可能会选择在以后的版本中更改此行为,然后您的测试将被清除。最好将当前的NUnit二进制文件检入Source Control。
  • VS(恕我直言,用他们的'敏捷工具鼓励错误行为')在他们的MS测试框架中称为“有序测试”。我没有浪费任何时间阅读,但似乎是针对相同的观众

另请参阅:characteristics of a good test

答案 14 :(得分:0)

如果您使用的是[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 Attribute

答案 15 :(得分:0)

在使用TestCaseSource的情况下,关键是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);
}

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

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. 这只是示例,用于说明如何在测试中获取名称和顺序,该顺序以数字/字母顺序进行,因此,如果您有十个以上的测试,则建议进行测试01,测试02 ....测试10,测试11之所以如此,是因为如果您进行了测试1,并且在某个时候进行了测试10,则该订单将成为测试1,测试10,测试2等。
  2. 输入和Expected可以是任何类型,字符串,对象或自定义类。
  3. 除了订购之外,这里的好处是您看到了更重要的测试名称。