As title said, I would like to use a combination of a TestCase
and TestCaseSource
/ValueSource
.
Here is an example:
public static string[] Source1 = new string[]
{
"One", "Two", "Three"
};
public static string[] Source2 = new string[]
{
"Four", "Five", "Six"
};
[TestCase("A"), TestCaseSource(nameof(Source1))]
[TestCase("B"), TestCaseSource(nameof(Source2))]
[TestCase("C"), TestCaseSource(nameof(Source1))]
[TestCase("C"), TestCaseSource(nameof(Source2))]
public void MyTest(string x, string y)
{
// ...
}
Of course this doesn't work, but what I would like to do is:
- Run Test Case A against Source1
- Run Test Case B against Source2
- Run Test Case C against both Source1 and Source2
Is it possible to achieve something like this?
CodePudding user response:
Long answer because I wanted to clarify the tradeoffs involved...
I'll answer the question as you asked it first: Is it possible to use a combination of TestCase and TestCaseSource/ValueSource in NUnit?
Yes, it is. NUnit lets you combine any of the sources for test case data on a method.
- TestCase gives you one test case
- TestCase source gives you multiple test cases
- ValueSource gives you data for one parameter (i.e. part of a test case)
In your example, you have provided a total of 16 test cases to the MyTest
method. All of them are, of course, invalid, because MyTest takes two arguments and each of your test cases provides only a single value.
It appears from the example that you expected attributes placed within the same brackets to group in some way. That's not how C# interprets them. What you wrote is exactly the same as writing this:
[TestCase("A")]
[TestCase("B")]
[TestCase("C")]
[TestCaseSource(nameof(Source1))]
[TestCaseSource(nameof(Source2))]
[TestCaseSource(nameof(Source1))]
[TestCaseSource(nameof(Source2))]
This is not an NUnit issue. It's just how C# attributes work.
So, while it's possible to combine the attributes on the same method, it's not possible to make the values from those attributes combine into test cases in the way you want.
NUnit does have a way to combine values combinatorially using the Values
or ValueSource
attribute, which apply to the individual parameters and not the method.
Using ValuesAttribute
, you could rewrite your example like this...
public void MyTest(
[Values("A", "B", "C")] string x,
[Values("One", "Two", "Three")] string y )
{
// ...
}
That almost works for you, but not quite. Nine cases are created, every possible combination of ("A", "B", "C")
and ("One", "Two", "Three")
. However, You want "B" to combine with "Four", "Five" and "Six" and "C" to combine with all six values of the second arguent. Neither Values
nor ValueSource
allow you to control things to that extent.
Which brings us back to TestCaseSource
. As suggested in a comment, you can use a method to supply test cases through the TestCaseSourceAttribute
. Here's one way you might write such a method...
static IEnumerable<TestCaseData> MySource()
{
var source1 = new [] { "One", "Two", "Three" };
var source2 = new [] { "Four", "Five", "Six" };
var source3 = source1.Concat(source2);
foreach (var y in source1)
yield return new TestCaseData("A", y);
foreach (var y in source2)
yield return new TestCaseData("B", y);
foreach (var y in source1)
yield return new TestCaseData("C", y);
}
That said, the above takes 12 lines, excluding blank lines, and generates 12 test cases. My own approach would be to avoid complexity in favor of clarity and use an array of TestCaseData items specifically enumerating what you want the cases to be, like...
var MySource = new [] {
new TestCaseData("A", "One"),
new TestCaseData("A", "Two"),
etc...
};
This has the advantage of being easy to understand by whomever looks at the code later on, including yourself a few months from now. :-)