4

I have extension method which tests if string is GUID.

public static bool IsGuid(this string str)
{
    if(str == null)
        throw new ArgumentNullException(str, "Argument can not be NULL");
    Guid guidFromString;
    return Guid.TryParse(str, out guidFromString);
}

I want test it via xUnit and Theory.
For string it's working:

[Theory, InlineData(""), InlineData(" ")]
public void IsGuid_EmptyOrWhiteSpace_ShouldReturnFalse(string str)
{
    // Arrange
    bool result;

    // Act
    result = str.IsGuid();

    // Assert
    Assert.False(result);
}

But how I can do it for array of Guids? I need test Guid.Empty' andGuid.NewGuid`.

This not work:

[Theory, MemberData(nameof(Guids))]
public void IsGuid_EmptyOrValidGuid_ShouldReturnTrue(string str)
{
    // Arrange
    bool result;

    // Act
    result = str.IsGuid();

    // Assert
    Assert.False(result);
}

public static IEnumerable<string> Guids
{
    get
    {
        yield return Guid.Empty.ToString();
        yield return Guid.NewGuid().ToString();
    }
}

@Edit Test fails because

System.ArgumentException
Property Guids on ExtensionsLibraryTests.StringExtensions.xUnitStringExtensionsTests yielded an item that is not an object[]
   at Xunit.MemberDataAttribute.ConvertDataItem(MethodInfo testMethod, Object item)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Xunit.Sdk.XunitTheoryTestCaseRunner.<AfterTestCaseStartingAsync>d__7.MoveNext()
Julian
  • 33,915
  • 22
  • 119
  • 174
Nerf
  • 938
  • 1
  • 13
  • 30
  • Hey.. maybe it's just that `yield return Guid.NewGuid().ToString();` returns a valid GUID and your test has Assert.False? To have this piece of code be the same as the previous one (`InlineData(""), InlineData(" ")`) you should `yield return ""; yield return " ";` instead of a valid Guid string.. – quetzalcoatl Mar 10 '17 at 11:00
  • Fixed AAA sections :) – Nerf Mar 10 '17 at 11:04
  • Possible duplicate: http://stackoverflow.com/questions/22093843/pass-complex-parameters-to-theory (sorry, I can't dup-vote, since I undone a previous 'unclear' vote..) – quetzalcoatl Mar 10 '17 at 11:11

1 Answers1

3

Your test method has currently just one argument - string - but that does not has to be the case. What if your test method had 3 arguments? How'd you pack it into a IEnumerable<what-here?>

For this reason, when you use 'property data' feature of xUnit, xUnit requires that property to be in form of IEnumerable<object[]>

public static IEnumerable<object[]> Guids
{
    get
    {
        yield return new object[]{ "" };
        yield return new object[]{ " " };
    }
}

That should solve the immediate problem. However, I'd encourage you to try out this layout:

[Theory, MemberData(nameof(Guids))]
public void thinkofsomesmartname(bool expectedResult, string text)
{
    bool result = text.IsGuid();

    Assert.Equal(expectedResult, result);
}

public static IEnumerable<object[]> Guids
{
    get
    {
        yield return new object[]{ false, "" };
        yield return new object[]{ false, " " };
        yield return new object[]{ true, Guid.NewGuid().ToString() };
    }
}

Of course, it's a bit of hack to pass the 'expected result' through the data set, and that makes inventing the test name a bit hard. You could create two data sets: wrong and good, and make two test methods one with Assert.False, and one with Assert.True.. but since it's very simple test and since it is heavily data-driven anyways, I like to write it that way.

By the way, this example also shows you why object[] and not just string in the IEnumerable: can have many parameters!

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107