【问题标题】:Test parameterization in xUnit.net similar to NUnit在 xUnit.net 中测试参数化,类似于 NUnit
【发布时间】:2012-02-24 23:52:25
【问题描述】:

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

这将在 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"; }

这将生成 5 个单独的测试并自动比较结果 (Assert.Equal())。

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

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

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

【问题讨论】:

标签: c# .net unit-testing nunit xunit.net


【解决方案1】:

xUnit 提供了一种通过称为数据理论的东西来运行参数化测试的方法。这个概念等同于 NUnit 中的概念,但开箱即用的功能并不完整。

这是一个例子:

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

在此示例中,xUnit 将为每个 InlineDataAttribute 运行一次 Should_format_the_currency_value_correctly 测试,每次将指定值作为参数传递。

数据理论是一个扩展点,您可以使用它来创建运行参数化测试的新方法。完成此操作的方法是创建新属性,这些属性检查并选择性地作用于测试方法的参数和返回值。

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

【讨论】:

  • 显然是not allowed使用十进制字面量作为属性参数。
  • @RubenBartelink 您的链接未找到。请转至此处:blog.benhall.me.uk/2008/01/introduction-to-xunit-net-extensions
  • 您需要 xUnit.net: Extensions(NuGet 包),否则 [Theory] 属性不可用。
  • 如果最推荐的 .NET 单元测试框架有一些文档就好了..
  • Google 说你的答案是 xUnit 文档。
【解决方案2】:

让我在这里再举一个例子,以防它为某人节省一些时间。

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

【讨论】:

    【解决方案3】:

    在您的第一个请求中,您可以按照here 找到的示例进行操作。

    您可以构造一个包含测试集合所需数据的静态类

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

    然后,使用 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);
        }
    }
    

    或者如果您使用的是 C# 6.0,

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

    MemberDataAttribute 的第一个参数允许您定义用作数据源的成员,因此您在重用方面具有相当大的灵活性。

    【讨论】:

      【解决方案4】:

      根据 xUnit 中的this article,您有三个“参数化”选项:

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

      InlineData 示例

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

      类数据示例

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

      会员数据示例

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

      【讨论】:

        【解决方案5】:

        我找到了一个库,它产生与 NUnit 的 [Values] 属性等效的功能,称为 Xunit.Combinatorial

        它允许您指定参数级值:

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

        或者您可以隐含地让它计算出覆盖所有可能组合的最小调用次数:

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

        【讨论】:

          【解决方案6】:

          我在这里接受了所有答案,另外还使用了 XUnit 的 TheoryData&lt;,&gt; 泛型类型,为我的测试中的“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 为 .NET Core 使用 VS2017(15.3.3)、C#7 和 XUnit 2.2.0

          【讨论】:

          • 这很可爱。
          猜你喜欢
          • 2011-01-22
          • 2012-01-31
          • 1970-01-01
          • 2014-08-22
          • 2011-06-16
          • 1970-01-01
          • 2010-10-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多