I'm setting up unit tests using Microsoft.VisualStudio.TestTools.UnitTesting
test attributes. I'm trying to pass a different number of elements into a params
array in my test method, as described in this answer.
Here's the relevant code in my [TestClass]
:
[TestClass]
public class ExtensionsTests
{
[TestMethod]
[DataRow(1, "")]
[DataRow(1, "stuff")]
[DataRow(1, "asdf", "ASDF", "asDF", "ASdF")]
[DataRow(3, "asdf", "ASDF", "1234", "d1sf5d1f")]
[DataRow(2, "asdf", "ASDF", "ásdf", "ÁSdF")] //a and á (accent) are different characters
public void DbDistinct_ShouldBeCaseInsensitive(int expectedCount, params string[] strs)
{
var distinctStuff = strs.DbDistinct();
Assert.AreEqual(expectedCount, distinctStuff.Count());
}
}
When I run the unit test, I get this exception before it even make it into the body of the test:
Test method ExtensionsTests.DbDistinct_ShouldBeCaseInsensitive threw exception: System.Reflection.TargetParameterCountException: Parameter count mismatch. at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(Action action)
Interestingly, when I look at the error for a test case where there is only a single string to be passed into the params array (like the [DataRow(1, "stuff")]
test case), I get a different error:
Test method ExtensionsTests.DbDistinct_ShouldBeCaseInsensitive threw exception: System.ArgumentException: Object of type 'System.String' cannot be converted to type 'System.String[]'. at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(Action action)
It looks like the DataRow is ignoring the params
part of the test method. How can I specify a variable number of test elements to be passed into my test method? My test project is .NET Framework 4.8 (C# 7.3). I get the same error in both Rider and VS2022, so I don't think this is IDE-related.
I think figured out why I ran into the Parameter mismatch or the invalid casting issues. The best guess I found based on a linked article on another Stack Overflow answer:
The DataRow
attribute has a constructor that doesn't play well with arrays because the constructor itself accepts params Object[]
. This means that if you have an array whose type could fill the role of Object[]
, the compiler prefers matching your array directly instead of expanding it as part of a params
array.
The suggested solution to explicitly wrap things properly (with a collection initializer) is not compatible with Attributes because everything in an Attribute must be a compile-time constant (and therefore initializers and new
don't work).
[DynamicData]
I ended up side-stepping this issue by using a DynamicData test cases. I defined a static property that generates my test cases. Each test case is defined as an object[]
, whose elements are injected as parameters in my test method (in order). I made each test case be an expected value (int
) and the test data (string[]
):
public static IEnumerable<object[]> CaseInsensitiveTestCases
{
get
{
return new[]
{
new object[] { 1, new string[] { "" } },
new object[] { 1, new string[] { "stuff" } },
new object[] { 1, new string[] { "asdf", "ASDF", "asDF", "ASdF" } },
new object[] { 3, new string[] { "asdf", "ASDF", "1234", "d1sf5d1f" } },
new object[] { 2, new string[] { "asdf", "ASDF", "ásdf", "ÁSdF" } }, //a and á (accent) are different characters
};
}
}
Then I just used [DynamicData]
attribute on my unit test method:
[TestMethod]
[DynamicData(nameof(CaseInsensitiveTestCases))]
public void DbDistinct_ShouldBeCaseInsensitive(int expectedCount, string[] strs)
{
var distinctStuff = strs.DbDistinct();
Assert.AreEqual(expectedCount, distinctStuff.Count());
}