62

I want to pass an array of string to one of my XUnit test method, but when I just do the following it doesn't work (array + params mechanism)

    [Theory]
    [InlineData(new object[] { "2000-01-02", "2000-02-01" })]
    public void TestSynchronizeMissionStaffing_PeriodNoMatch(string[] dateStrings)

I can work around the issue like this:

    [Theory]
    [InlineData(0, new object[] { "2000-01-02", "2000-02-01" })]
    public void TestSynchronizeMissionStaffing_PeriodNoMatch(int dummy, string[] dateStrings)

But I'm hoping there something better to resolve the issue.

Can you tell me?

Serge Intern
  • 2,669
  • 3
  • 22
  • 39
  • The number of string inside the array could be anything. – Serge Intern Apr 05 '16 at 07:18
  • it's 2018 and still this is the only workaround that's worked for me. Should really open an issure for xUnit – yair Jan 03 '18 at 12:47
  • fix that last comment to WAS the only workaround. It appears there's actually [a straightforward solution](https://stackoverflow.com/a/48100749/978502) – yair Jan 06 '18 at 20:58

8 Answers8

71

Use params before the method's string[] argument, and then you won't need to initialize a string[] in InlineData attribute, rather you could use a variable number of string literals, for which the compiler doesn't complain one bit:

[Theory]
    [InlineData("2000-01-02", "2000-02-01")]
    public void TestSynchronizeMissionStaffing_PeriodNoMatch(params string[] dateStrings)
yair
  • 8,945
  • 4
  • 31
  • 50
43

For the benefit of searchers - in 2020, this is possible without params or object casting.

The key to getting it to play nicely seems to be to write the method signature before writing the InlineData parts. Here's some working code:

[Theory]
[InlineData(true, "expected", new string[] { "expected", "another" })]
[InlineData(false, "nope!", new string[] { "expected", "another" })]
public async void StringCheck_WithInputs_ExpectResultsMatch(bool expectedResult, string expectedString, string[] possibleStrings)
{
    bool actualResult = false;

    foreach(var row in possibleStrings)
    {
        if(row == expectedString)
        {
            actualResult = true;
        }
    }

    Assert.Equal(expectedResult, actualResult);
}
JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123
  • 7
    Actually, when you have only one parameter (the array) it doesn't work, but when you have more parameters than one array, then it works just fine. – Jacob May 19 '20 at 12:02
  • 3
    UPDATE (xunit 2.4.1 / xunit.runner.visualstudio 2.4.2): Now it's working just with the array parameter @Jacob – Maicon Heck Jul 25 '20 at 18:59
  • 3
    @MaiconHeck, what Jacob said appears to hold true still. I am using _xunit 2.4.1_ and _xunit.runner.visualstudio 2.4.3_ and visual studio complains unless I start the InlineData() attribute with some primitive argument. – Sal Alturaigi Mar 09 '21 at 15:54
  • Experimenting with this, the order of your parameters is important. Adding the string[] array last works, putting it first fails. – Yablargo May 28 '21 at 15:22
  • I'm not sure how people are making this work as putting "new string[]" results CS0182 (attribute argument must be a constant) – Matt M Dec 20 '21 at 15:46
  • 4
    @MattM - They're making it work by putting a dummy attribute before it, e.g., `public void MyTest(int iLiterallyDoNotCare, string [] importantArray)` and `[InlineData(666, new string [] { "Puppies", "Kittens" })]` – Red Jan 26 '22 at 16:06
  • 1
    This saved me so much confusion! I was wondering why it worked on one method and not the other. – ladygargar Dec 06 '22 at 12:12
7

This should work

[Theory]
[InlineData(new object[] { new string[] { "2000-01-02", "2000-02-01" } })]
public void TestSynchronizeMissionStaffing_PeriodNoMatch(string[] dateStrings)

When u initialize an object array like you did all elements on it is a single object, so when you trying to pass string array as parameter it passes a first element of the object array which is "2000-01-02".

5

This is a C# params feature in which an array is expanded. so xunit fails to input it to your one argument, you can cast the array to force it, like this:

[InlineData((object)(new object[] { "2000-01-02", "2000-02-01" }))] see also here.

Community
  • 1
  • 1
robi-y
  • 1,687
  • 16
  • 25
  • works for me... would you like to share a gist/repo? – robi-y Apr 05 '16 at 09:06
  • I've attached a screenshot showing that the array is passed. https://gist.github.com/anonymous/af6fc4095705228909de923e25ac0645#gistcomment-1742472 – robi-y Apr 05 '16 at 12:09
  • I use VS studio 2015 community, Resharper 10.2, XUnit 2.1.0. Do you have a clue why we get beferent behaviour? – Serge Intern Apr 05 '16 at 12:17
  • Haven't tried (yet) this env. some related issue: https://github.com/xunit/xunit/issues/763 you might want to discuss over there. Thanks for the vote, please update if you find something – robi-y Apr 05 '16 at 21:37
3

You can use the ClassData or MemberData attributes. These allow you to specify a method which returns the data you need

The method can be in a separate class (ClassData) or a method in the same class as the test (MemberData)

this explains it nicely http://hamidmosalla.com/2017/02/25/xunit-theory-working-with-inlinedata-memberdata-classdata/

ChrisCa
  • 10,876
  • 22
  • 81
  • 118
0

I believe it's a bug of XUnit. I've reported here:

https://github.com/xunit/xunit/issues/2456

Weifen Luo
  • 603
  • 5
  • 13
0

Whenever you need to pass a string array as argument to your Test, you can use your code, just add the "params" keyword to your string array prepended to it. It works just fine.

[Theory]
[InlineData(new object[] { "2000-01-02", "2000-02-01" })]
public void TestSynchronizeMissionStaffing_PeriodNoMatch(params string[] dateStrings)
{
   //Assert.[YourVerifier]();
}
0

I am using Xunit version 2.4.1 and with ChrisCa's answer above. I was able to simply do this below, that way the data can be separated from the test(s).


public class NoGenTests
{
    [Theory]
    [MemberData(nameof(NoGenTestsData.Nos), MemberType = typeof(NoGenTestsData))]
    public void NoGenTest(int size, int[] nos)

    {
         /*
        foreach (var item in nos)
        {
            Console.WriteLine("Item is " + item);
        }*/
        Assert.Equal(size, nos.Length);
    }

}

public class NoGenTestsData
{

    public static object[] Nos =
    {
            new object[] {3, new int[] {2, 3, 5}},
            new object[] {6, new int[] {2, 3, 5, 7, 11, 13}},
            new object[] {7, new int[] {2, 3, 5, 7, 11, 13, 17}},
            new object[] {8, new int[] {2, 3, 5, 7, 11, 13, 17, 19}},
            new object[] {2, new int[] {2, 3}},
            new object[] {1, new int[] {2}}
        };
}

Ragavendra
  • 90
  • 5