Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to use NUnit TestCaseAttribute with ValuesAttribute together?

Tags:

c#

nunit

I am using intensively NUnit TestCase attribute. For some of my tests are annotated with 20+ TestCase attributes defining 20+ test cases. However I would like to test all the 20 test cases say with an extra value what could be 1 or 0. This means for me different test cases. This easily could be implemented with ValuesAttribute:

My current state:

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

I would like to do something similar to this (what I can not:)

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

Why I would like to do this? Because these 40+ combination means different test cases. Unfortunately NUnit does not allow using [TestCase] and [Values] attributes together, the test runner expects exactly the same number of parameters as it listed in TestCaseAttribute. (I can understand the architect, but still...) The only thing I could figure out was this:

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

So I ended up to be forced to commit the sin of the copy and paste, and I duplicated the TestCases, now I have 40+. There must be some way... What if not only (0,1) the range of the value but 0,1,2,3. We are ending with 80+ copied testcases?

Missed I something?

Thx in advance

like image 916
g.pickardou Avatar asked May 31 '14 21:05

g.pickardou


1 Answers

This came up as a first page search result for me despite its age, but I wanted something lighter than the existing solution.

Using another ValuesAttribute or ValueSourceAttribute for the test cases allows combinations, but the trick is to get a set of linked values as a single case when using them - each affects only a single named parameter.

Attributes require compile-time constant inputs. That allows you to make a literal array, but if your values are of different types, you'll have to make the array a common base type, like object. You'll also have to access the items by index. I like short, obvious unit tests; parsing the array makes the test look busy and messy.

One terse solution is to use a ValueSource attribute with a static method providing tuples. This method can immediately precede the test method, and is only a little more verbose than using the TestCase attribute. Using the code from the question:

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)

The parameter following the ValueSource is a tuple named testCase, and its contents are named to match the original test case arguments. To reference those values in the test, precede it with the name of the tuple (e.g. testCase.result instead of just result).

As written here, six tests will run - one for each possible combination of anyName and testCase.

I don't mind tuples for simple data like in this example, but I went a little further and defined a very simple class to use in place of the tuples. Usage is basically the same, except you don't name the members within the parameter.

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)

The test case class definition could be moved to the bottom of the test class, or to a separate file. Personally, I prefer to put both at the bottom of the test class - you can always peek the definition when you want to see it next to the test.

like image 128
AjimOthy Avatar answered Sep 21 '22 17:09

AjimOthy