简体   繁体   English

在xUnit.net中测试与NUnit类似的参数化

[英]Test parameterization in xUnit.net similar to NUnit

Are there any means in xUnit.net framework similar to the following features of NUnit? xUnit.net框架中是否有类似于NUnit的以下功能的方法?

[Test, TestCaseSource("CurrencySamples")]
public void Format_Currency(decimal value, string expected){}

static object[][] CurrencySamples = new object[][]
{
    new object[]{ 0m, "0,00"},
    new object[]{ 0.0004m, "0,00"},
    new object[]{ 5m, "5,00"},
    new object[]{ 5.1m, "5,10"},
    new object[]{ 5.12m, "5,12"},
    new object[]{ 5.1234m, "5,12"},
    new object[]{ 5.1250m, "5,13"}, // round
    new object[]{ 5.1299m, "5,13"}, // round
}

This will generate 8 separate tests in NUnit GUI 这将在NUnit GUI中生成8个单独的测试

[TestCase((string)null, Result = "1")]
[TestCase("", Result = "1")]
[TestCase(" ", Result = "1")]
[TestCase("1", Result = "2")]
[TestCase(" 1 ", Result = "2")]
public string IncrementDocNumber(string lastNum) { return "some"; }

This will generate 5 separate tests and automatically compare the results ( Assert.Equal() ). 这将生成5个单独的测试,并自动比较结果( Assert.Equal() )。

[Test]
public void StateTest(
    [Values(1, 10)]
    int input,
    [Values(State.Initial, State.Rejected, State.Stopped)]
    DocumentType docType
){}

This will generate 6 combinatorial tests. 这将生成6个组合测试。 Priceless. 无价。

Few years ago I tried xUnit and loved it but it lacked these features. 几年前,我尝试并喜欢xUnit,但它缺少这些功能。 Can't live without them. 没有他们就无法生存。 Has something changed? 有什么改变吗?

xUnit offers a way to run parameterized tests through something called data theories . xUnit通过一种称为数据理论的方法提供了运行参数化测试的方法。 The concept is equivalent to the one found in NUnit but the functionality you get out of the box is not as complete. 该概念与NUnit中的概念相同,但是开箱即用的功能并不完整。

Here's an example: 这是一个例子:

[Theory]
[InlineData("Foo")]
[InlineData(9)]
[InlineData(true)]
public void Should_be_assigned_different_values(object value)
{
    Assert.NotNull(value);
}

In this example xUnit will run the Should_format_the_currency_value_correctly test once for every InlineDataAttribute each time passing the specified value as argument. 在此示例中,每次将指定值作为参数传递时,xUnit都会为每个InlineDataAttribute运行Should_format_the_currency_value_correctly测试一次。

Data theories are an extensibility point that you can use to create new ways to run your parameterized tests. 数据理论是可扩展点 ,可用于创建运行参数化测试的新方法。 The way this is done is by creating new attributes that inspect and optionally act upon the arguments and return value of the test methods. 完成此操作的方式是创建新的属性 ,这些属性可以检查测试方法的自变量和返回值,并可以选择对它们起作用。

You can find a good practical example of how xUnit's data theories can be extended in AutoFixture 's AutoData and InlineAutoData theories. 您可以在AutoFixtureAutoDataInlineAutoData理论中找到有关如何扩展xUnit数据理论的很好的实际示例。

Let me throw one more sample here, just in case it saves some time to someone. 让我在这里再扔一个样本,以防万一可以节省一些时间。

[Theory]
[InlineData("goodnight moon", "moon", true)]
[InlineData("hello world", "hi", false)]
public void Contains(string input, string sub, bool expected)
{
    var actual = input.Contains(sub);
    Assert.Equal(expected, actual);
}

On your first request, you can follow the examples found here . 根据您的第一个请求,您可以按照此处的示例进行操作。

You can construct a static class containing the data necessary for a collection of tests 您可以构造一个静态类,其中包含测试集合所需的数据

using System.Collections.Generic;

namespace PropertyDataDrivenTests
{
    public static class DemoPropertyDataSource
    {
        private static readonly List<object[]> _data = new List<object[]>
            {
                new object[] {1, true},
                new object[] {2, false},
                new object[] {-1, false},
                new object[] {0, false}
            };

        public static IEnumerable<object[]> TestData
        {
            get { return _data; }
        }
    }
}

Then, using the MemberData attribute, define the test as such 然后,使用MemberData属性将测试定义为

public class TestFile1
{
    [Theory]
    [MemberData("TestData", MemberType = typeof(DemoPropertyDataSource))]
    public void SampleTest1(int number, bool expectedResult)
    {
        var sut = new CheckThisNumber(1);
        var result = sut.CheckIfEqual(number);
        Assert.Equal(result, expectedResult);
    }
}

or if you're using C# 6.0, 或者如果您使用的是C#6.0,

[Theory]
[MemberData(nameof(PropertyDataDrivenTests.TestData), MemberType = typeof(DemoPropertyDataSource))]

The first argument of MemberDataAttribute allows you to define the member you use as a datasource, so you have a fair amount of flexibility on reuse. MemberDataAttribute的第一个参数允许您定义用作数据源的成员,因此在重用方面具有相当大的灵活性。

I found a library that produces equivalent functionality to NUnit's [Values] attribute called Xunit.Combinatorial : 我找到了一个库,该库产生与NUnit的[Values]属性等效的功能,称为Xunit.Combinatorial

It allows you to specify parameter-level values: 它允许您指定参数级别的值:

[Theory, CombinatorialData]
public void CheckValidAge([CombinatorialValues(5, 18, 21, 25)] int age, 
    bool friendlyOfficer)
{
    // This will run with all combinations:
    // 5  true
    // 18 true
    // 21 true
    // 25 true
    // 5  false
    // 18 false
    // 21 false
    // 25 false
}

Or you can implicitly have it figure out the minimal number of invocations to cover all possible combinations: 或者,您可以隐式地计算出涵盖所有可能组合的最少调用次数:

[Theory, PairwiseData]
public void CheckValidAge(bool p1, bool p2, bool p3)
{
    // Pairwise generates these 4 test cases:
    // false false false
    // false true  true
    // true  false true
    // true  true  false
}

According to this article in xUnit you have three "parametrization" options: 根据xUnit中的本文 ,您可以使用三个“参数化”选项:

  1. InlineData 内联数据
  2. ClassData 类数据
  3. MemberData 会员资料

InlineData example InlineData示例

[Theory]
[InlineData(1, 2)]
[InlineData(-4, -6)]
[InlineData(2, 4)]
public void FooTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

ClassData example ClassData示例

public class BarTestData : IEnumerable<object[]>
{
    public IEnumerator<object[]> GetEnumerator()
    {
        yield return new object[] { 1, 2 };
        yield return new object[] { -4, -6 };
        yield return new object[] { 2, 4 };
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}


[Theory]
[ClassData(typeof(BarTestData))]
public void BarTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

MemberData example MemberData示例

[Theory]
[MemberData(nameof(BazTestData))]
public void BazTest(int value1, int value2)
{
    Assert.True(value1 + value2 < 7)
}

public static IEnumerable<object[]> BazTestData => new List<object[]>
    {
        new object[] { 1, 2 },
        new object[] { -4, -6 },
        new object[] { 2, 4 },
    };

I took on-board all the answers here and additionally made use of XUnit's TheoryData<,> generic types to give me simple, easy to read and type safe data definitions for the 'MemberData' attribute on my test, as per this example: 我接受了这里的所有答案,并另外使用了TheoryData<,>TheoryData<,>泛型类型,使我在测试中为“ MemberData”属性提供简单,易于阅读的类型安全的数据定义,如下例所示:

/// must be public & static for MemberDataAttr to use
public static TheoryData<int, bool, string> DataForTest1 = new TheoryData<int, bool, string> {
    { 1, true, "First" },
    { 2, false, "Second" },
    { 3, true, "Third" }
};

[Theory(DisplayName = "My First Test"), MemberData(nameof(DataForTest1))]
public void Test1(int valA, bool valB, string valC)
{
    Debug.WriteLine($"Running {nameof(Test1)} with values: {valA}, {valB} & {valC} ");
}

从测试浏览器观察到的三个测试运行是“我的第一个测试”


NB Using VS2017(15.3.3), C#7, & XUnit 2.2.0 for .NET Core NB使用VS2017(15.3.3),C#7和XUnit 2.2.0 for .NET Core

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM