question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cannot provide 2 arrays to DataRow

See original GitHub issue

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

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
Evangelinkcommented, Oct 13, 2022

DisplayName is settable, but that does not solve the problem imho. To generate DisplayName I need to get the input data (method and params) and I get those in GetDisplayName.

I understand although for DataRow, the elements are just inline so that’s super easy to have access to them. For example: [DataRow(new[] { "a" }, new[] { "b" }, DisplayName= "MyMethod ([a], [b]")].

I still think it’s easy enough to make GetDisplayName virtual and allow for custom behavior so I have created a new ticket to track this part: https://github.com/microsoft/testfx/issues/1336

0reactions
nohwndcommented, Oct 13, 2022

I have done some investigation on this and the problem is not the same as #1016 (there issue is linked to test ID uniqueness) while here error is compile time error and is linked to the signature of DataRowAttribute.

Not sure if we are talking about the same problem. I was saying that I see weird behavior in version 2.3.0 and I think that it is already captured in issue #1016.

As far as I can see, there is a DisplayName property that is settable so you should be able to manually override the name.

DisplayName is settable, but that does not solve the problem imho. To generate DisplayName I need to get the input data (method and params) and I get those in GetDisplayName.

I also most likely want to rely on the base implementation of GetDisplayName, and only add some additional info, not re-implement the whole method from scratch or hide it. So imho GetDisplayName should be virtual.

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - How to supply two arrays as DataRow parameters?
In your expression, I think C# takes the second string array as the object[] moreData and it doesn't like it. If you specify...
Read more >
Array as datarow attribute parameter - Build/Test Issues
Hello, There is problem in datarow attribute for unit test method. NCrunch can not substitute array parameter into test method. For example:
Read more >
DataRow Array Creation or Another Method - Help
I´d need to create an array of datarows and later on i need to pass every individual cell value to another system.
Read more >
C# Test Unit DataRow max number of arguments reduced ...
Hello, I have unit tests with 30 parameters which where working last year. Now VS (17.6.0 Preview 2) doesn't compile and fires (I...
Read more >
Initialising New Data array datarow of string Windows version
Hi There, I am updating a process from Legacy to Windows and getting an error with adding an array datarow {Strings}; Please help...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found