C# 在 xUnit.net 中测试参数化类似于 NUnit

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9110419/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 06:00:14  来源:igfitidea点击:

Test parameterization in xUnit.net similar to NUnit

c#.netunit-testingnunitxunit.net

提问by UserControl

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. Priceless.

这将生成 6 个组合测试。无价。

Few years ago I tried xUnit and loved it but it lacked these features. Can't live without them. Has something changed?

几年前,我尝试过 xUnit 并喜欢它,但它缺少这些功能。没有他们就活不下去。有什么改变了吗?

采纳答案by Enrico Campidoglio

xUnitoffers a way to run parameterized teststhrough something called data theories. The concept is equivalent to the one found in NUnit but the functionality you get out of the box is not as complete.

xUnit提供了一种通过称为数据理论的东西来运行参数化测试的方法。该概念与 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_correctlytest once for every InlineDataAttributeeach time passing the specified value as argument.

在这个例子中Should_format_the_currency_value_correctly,每次InlineDataAttribute将指定的值作为参数传递时,xUnit 都会运行一次测试。

Data theories are an extensibility pointthat you can use to create new ways to run your parameterized tests. The way this is done is by creating new attributesthat 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 AutoDataand InlineAutoDatatheories.

您可以在AutoFixtureAutoDataInlineAutoData理论中找到一个很好的实际示例,说明如何扩展 xUnit 的数据理论。

回答by Sevenate

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

回答by LewisM

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 的第一个参数允许您定义用作数据源的成员,因此您在重用方面具有相当大的灵活性。

回答by Adam

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
}

回答by Peter

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:

我采纳了这里的所有答案,另外还使用了 XUnit 的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} ");
}

Three tests runs observed from test explorer for 'My First Test'

从“我的第一次测试”的测试资源管理器中观察到的三个测试运行



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

回答by itim

According to this articlein xUnit you have three "parametrization" options:

根据xUnit 中的这篇文章,您有三个“参数化”选项:

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

InlineData example

内联数据示例

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

ClassData example

类数据示例

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

会员数据示例

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