Skip to content

Cannot provide 2 arrays to DataRow #1180

@nohwnd

Description

@nohwnd

Description

DataRow does not allow providing 2 array parameters..

Steps to reproduce

Use [DataRow(new[] { "a" }, new[] {"b"} )] in a test.

Expected behavior

I can provide any reasonable number of parameters in the same form. Possibly there are constructor overloads that take 1 - 7 parameters for DataRow, and DataRow is not sealed so I can add more.

Actual behavior

You get error:

CS0182 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

This is confusing because we do provide types that fit this criteria, and because using three arrays works just fine.
[DataRow(new[] { "a" }, new[] { "b" }, new[] { "c" })]

In reality you are running into the implementation detail of DataRow which uses params and that makes array mismatch between the inferred string[] and the destination object[] types.

You can work around the issue by wrapping your parameters into additional array, or by subclassing DataRow:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    // [DataRow(new[] { "a" }, new[] { "b" })] // Does not compile
    [DataRow(new[] { "a" }, new object[] { new[] { "b" } })]
    [DataRow2(new[] { "c" }, new[] { "d" })]
    public void TestMethod1(string[] _, string[] __)
    {
    }

    [TestMethod]
    [DataRow(new[] { "a" }, new[] { "b" }, new[] { "c" })]
    [DataRow2(new[] { "d" }, new[] { "e" }, new[] { "f" })]
    public void TestMethod2(string[] _, string[] __, string[] ___)
    {
    }
}

public class DataRow2Attribute : DataRowAttribute
{
    public DataRow2Attribute(object data1) : base(data1) { }
    public DataRow2Attribute(object data1, object data2) : base(data1, new[] { data2 }) { }
    public DataRow2Attribute(object data1, object data2, object data3) : base(data1, new[] { data2, data3 }) { }
    public DataRow2Attribute(object data1, object data2, object data3, object data4) : base(data1, new[] { data2, data3, data4 }) { }
}

This still does not fix the issue fully, because the name of the method is translated to  TestMethod2 (System.String[],System.String[],System.String[]), which makes only one of the row to run per method, not both.

Get display name is not overridable, so unless I want to implement the whole attribute myself I don't have an easy way to override it.

Possible implementation of GetDisplayName would look into all the arrays and expand them.

        public string GetDisplayName(MethodInfo methodInfo, object[] data)
        {
            if (!string.IsNullOrWhiteSpace(DisplayName))
            {
                return DisplayName!;
            }

            var stringBuilder = new StringBuilder(methodInfo.Name);
            stringBuilder.Append('(');

            // Add params.
            var first = true;
            foreach (var d in data)
            {
                if (!first)
                {
                    stringBuilder.Append(", ");
                }

                first = false;

                if (d.GetType().IsArray)
                {
                    stringBuilder.Append("@(");
                    var first2 = true;
                    foreach (var v in (IEnumerable)d)
                    {
                        if (!first2)
                        {
                            stringBuilder.Append(", ");
                        }

                        first2 = false;
                        stringBuilder.Append(v?.ToString() ?? "<null>");
                    }
                    stringBuilder.Append(')');
                }
                else
                {
                    stringBuilder.Append(d);
                }
            }

            stringBuilder.Append(')');

            return stringBuilder.ToString();
        }

Environment

Please share additional details about the test environment.
Windows,

<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />

❗ Latest 2.3.0-preview has a regression, where it only runs the last of the tests, even when the names are unique.

image

AB#1641808

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions