2

When creating an FsCheck.Xunit unit test with string input I am struggling with frequent occurrences of strings containing "\0" which I believe feed into a C library and lead to string truncation. Strings containing "\0" are created frequently by FsCheck as you will find if you run the test below.

What is the simplest possible way to adjust the string generator to avoid strings containing "\0"? I need this behaviour across multiple tests and am using .NET Core.

BR, Mark

public class NewTests
{

    [Property(Verbose = true)]
    public Property Test1(string myString)
    {
        return (!myString.Contains("\0")).ToProperty();
    }

}
MFL
  • 69
  • 1
  • 8

1 Answers1

1

The simplest possible way is to just filter it out inside your test, e.g. using a helper method. However note that string itself can also be null.

If you want strings that can be null but contain no null characters:

[Property]
public bool TestIt(StringNoNulls s)
{
    return s.Item == null || !s.Item.Contains("\0");
}

If you want non-null strings:

[Property]
public bool TestIt(NonNull<string> s)
{
    return s != null;
}

If you want both I have nothing out of the box! But, you can do something like:

public class CustomArbs
{
    public static Arbitrary<string> ReallyNoNullsAnywhere()
    {
        return Arb.Default.String().Filter(s => s != null && !s.Contains("\0"));
    }
}

[Property(Arbitrary = new[] { typeof(CustomArbs) })]
public bool TestIt(string s)
{
    return s != null && !s.Contains("\0");
}

There's also PropertiesAttribute which you can put on a class to override all Arbitrary instances of a particular set of types on all properties in that class, so you don't have to add the Arbitrary argument on each test method.

A pattern that I end up using often is not to override the Arbitrary<string> instance itself, but make a wrapper type, so it then becomes clear in the signuature what sort of string I'm getting:

public class AntiNullString
{
    public string Get { get; }

    public AntiNullString(string s)
    {
        Get = s;
    }
}

public class CustomArbs
{
    public static Arbitrary<AntiNullString> ReallyNoNullsAnywhere()
    {
        return Arb.Default.String()
            .Filter(s => s != null && !s.Contains("\0"))
            .Convert(s => new AntiNullString(s), ans => ans.Get);
    }
}

[Property(Arbitrary = new[] { typeof(CustomArbs) })]
public bool TestIt(AntiNullString s)
{
    return s.Get != null && !s.Get.Contains("\0");
}
Kurt Schelfthout
  • 8,880
  • 1
  • 30
  • 48
  • Hi Kurt, thanks very much for the reply. Trying to follow your last suggestion, I created the class below. Is this the best way to avoid nulls and null chars? Any advice to change this appreciated! BR, Mark ``` public class StringNotNullAndContainsNoNulls { private static Arbitrary ArbObject { get; } = Arb.Default.StringWithoutNullChars().Filter(s => !(s is null)); public string Item => ArbObject.Generator.ToString(); } ``` – MFL Apr 24 '20 at 22:50
  • [Update to previous comment] Slightly better formatting: `public class StringNotNullAndContainsNoNulls { private static Arbitrary ArbObject { get; } = Arb.Default.StringWithoutNullChars().Filter(s => !(s is null)); public string Item => ArbObject.Generator.ToString(); }` – MFL Apr 24 '20 at 22:57
  • Not exactly, I've added an example of what I mean in the answer. – Kurt Schelfthout Apr 25 '20 at 17:17