是否有任何方法使用NUnit TestCaseAttribute与ValuesAttribute一起

本文关键字:TestCaseAttribute ValuesAttribute 一起 NUnit 任何 方法 是否 | 更新日期: 2023-09-27 18:06:00

我正在密集使用NUnit TestCase属性。因为我的一些测试用20多个TestCase属性进行了注释,定义了20多个测试用例。然而,我想测试所有的20个测试用例,说一个额外的值可能是1或0。这对我来说意味着不同的测试用例。这可以很容易地实现ValuesAttribute:

我的当前状态:

[TestCase(10, "Hello", false)] // 1
[TestCase(33, "Bye", true)]    // 2
// imagine 20+ testcase here)]
[TestCase(55, "CUL8R", true)]    // 20+
public void MyTest(int number, string text, bool result)

我想做类似的事情(我不能:)

[TestCase(10, "Hello", false)] // 1
[TestCase(33, "Bye", true)]    // 2
// imagine 20+ testcase here)]
[TestCase(55, "CUL8R", true)]    // 20+
public void MyTest([Values(0,1)] int anyName, int number, string text, bool result)

为什么我想这样做?因为这40多个组合意味着不同的测试用例。不幸的是,NUnit不允许同时使用[TestCase]和[Values]属性,测试运行器期望TestCaseAttribute中列出的参数数量完全相同。(我能理解建筑师的意思,但是……)我唯一能想到的是:

[TestCase(1, 10, "Hello", false] // 1
[TestCase(1, 33, "Bye", true]    // 2
// imagine 20+ testcase here]
[TestCase(1, 55, "CUL8R", true]  // 20
[TestCase(0, 10, "Hello", false] // 21
[TestCase(0, 33, "Bye", true]    // 22
// imagine 20+ testcase here]
[TestCase(0, 55, "CUL8R", true]  // 40
public void MyTest(int anyName, int number, string text, bool result)

所以我最终被迫犯了复制粘贴的错误,我复制了testcase,现在我有40多个。一定有什么办法……如果值的范围不只是(0,1)而是0,1,2,3。我们以80多个复制的测试用例结束?

我错过了什么?

提前致谢

是否有任何方法使用NUnit TestCaseAttribute与ValuesAttribute一起

这是我的第一页搜索结果,尽管它的年龄,但我想要一些比现有的解决方案更轻松。

为测试用例使用另一个ValuesAttributeValueSourceAttribute允许组合,但技巧是在使用它们时获得一组链接值作为单个用例-每个只影响单个命名参数。

属性需要编译时常量输入。这允许您创建一个文字数组,但是如果您的值是不同类型的,则必须将该数组设置为通用基类型,如object。您还必须通过索引访问项目。我喜欢简短、明显的单元测试;解析数组会让测试看起来很忙很乱。

一个简洁的解决方案是使用ValueSource属性和提供元组的静态方法。该方法可以立即在测试方法之前执行,并且只比使用TestCase属性稍微冗长一些。使用问题中的代码:
public static (int, string, bool)[] Mytest_TestCases()
{
    return new[] {
        (10, "Hello", false),
        (33, "Bye", true),
        (55, "CUL8R", true)
    };
}
[Test]
public void Mytest(
    [Values(0,1)] int anyName,
    [ValueSource(nameof(Mytest_TestCases))] (int number, string text, bool result) testCase)

ValueSource后面的参数是一个名为testCase的元组,其内容的命名与原始测试用例参数相匹配。为了在测试中引用这些值,在它前面加上元组的名称(例如testCase.result而不仅仅是result)。

正如这里所写的,将运行六个测试- anyNametestCase的每种可能组合各一个。

我不介意像这个例子中的简单数据的元组,但是我更进一步,定义了一个非常简单的类来代替元组。用法基本相同,除了不命名形参中的成员。

public class Mytest_TestCase
{
    public Mytest_TestCase(int number, string text, bool result)
    {
        Number = number;
        Text = text;
        Result = result;
    }
    public int Number;
    public string Text;
    public bool Result;
}
public static Mytest_TestCase[] Mytest_TestCases()
{
    return new[] {
        new Mytest_TestCase(10, "Hello", false),
        new Mytest_TestCase(33, "Bye", true),
        new Mytest_TestCase(55, "CUL8R", true)
    };
}
[Test]
public void Mytest(
    [Values(0,1)] int anyName,
    [ValueSource(nameof(Mytest_TestCases))] Mytest_TestCase testCase)

测试用例类定义可以移动到测试类的底部,或者移动到一个单独的文件中。就我个人而言,我更喜欢把它们放在测试类的底部——当你想在测试旁边看到定义时,你总是可以偷看它。

首先,提供测试数据的类本身就是测试代码,因此是第一类代码,应该这样维护。

如果你给类一个有意义的名字,我认为它可以是可读的和可维护的,也许更多,因为你的测试数据在一个地方。

这是我将如何实现你的情况:

  1. 创建一个类,我们将其命名为TestCaseGenerator,具有int, stringbool属性。它看起来像这样:

     public class TestCaseGenerator : IEnumerable {
         #region Attributes
         internal static List<TestCase> TestCases { get; set; }
         internal int myInt { get; set; }
         internal string myString { get; set; }
         internal bool myBoolean { get; set; }
         #endregion
         static TestCaseGenerator() {
             var json = <jsonRepresentationOfMyTestData>
             TestCases = JsonConvert.DeserializeObject<List<TestCase>>(json);    
         }
         public IEnumerator GetEnumerator() {
             return TestCases != null
                       ? TestCases.Select(x => new TestCase(x.myInt, x.myString, x.myBool) ?? "null")).GetEnumerator()
                       : null;
         }
      }
    
  2. 像这样构造包含测试的类:

     public class MyTestClass {
         [TestCaseSource(typeof(TestCaseGenerator))]
         public void MyTest(TestCase case) {
             // Do useful things with case.myInt, case.myString and case.myBool
         }
     }
    

一旦你有了这个基础结构,就没有什么好说的了,TestCaseGenerator.GetEnumerator()TestCase对象的列表上返回一个枚举器,就不能以返回两个较短列表的组合列表的方式编写。

创建一个组合的可枚举对象(借鉴这篇文章)将像这样:

class Program {
    static void Main(string[] args) {
        var firstArray = new object[][] { new object[] { 1, "A", false },
                                          new object[] { 2, "B", false },
                                          new object[] { 3, "C", false },
                                          new object[] { 4, "D", true },
                                          new object[] { 5, "E", true }
                                       };
        var secondArray = new object[] { 6, 7 };
        var joiner = new Joiner();
        IEnumerable<IEnumerable<object>> result = joiner.Join(firstArray.ToList(), secondArray.ToList());
        //result = new object[] { new object[] { 6, 1, "A", false },
        //                        new object[] { 7, 1, "A", false },
        //                        new object[] { 6, 2, "B", false },
        //                        new object[] { 7, 2, "B", false },
        //                        new object[] { 6, 3, "C", false },
        //                        new object[] { 7, 3, "C", false },
        //                        new object[] { 6, 4, "D", true },
        //                        new object[] { 7, 4, "D", true },
        //                        new object[] { 6, 5, "E", true },
        //                        new object[] { 7, 5, "E", true }
        //                      };
    }
}
public class Joiner
{
    public IEnumerable<IEnumerable<object>> Join(IEnumerable<IEnumerable<object>> source1, IEnumerable<object> source2) {
        foreach (IEnumerable<object> s1 in source1) {
            foreach (object s2 in source2) {
                yield return (new[] { s2 }).Concat(s1).ToArray();
            }
        }
    }
}
相关文章:
  • 没有找到相关文章