Adjust string generator to avoid "\0" with FsCheck.Xunit in C#

328 Views Asked by At

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();
    }

}
1

There are 1 best solutions below

3
Kurt Schelfthout On BEST ANSWER

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");
}