如何为FsCheck测试生成空字符串

时间:2014-04-22 17:06:20

标签: c# c#-to-f# fscheck

使用FsCheck Haskell QuickCheck测试库的F#版本,从C#生成测试,我发现随机字符串生成器不会生成空字符串。

using FsCheck.Fluent;
Spec.ForAny<string>(s => s != null).QuickCheck(); // always pass

此外,似乎没有按设计处理空字符串,但我没有设法将其固定from the documentation。例如,只需在两个字符串之间进行选择,其中一个为null,就不起作用了:

var strings = Any.ValueIn<string>(null, "non-null string");
Spec.For(strings, s => true).QuickCheck(); // throws null ref exception

字符串似乎是一种特殊情况,因为它处理自定义对象,例如

class Thing {}

与空值混合时:

var objects = Any.ValueIn(null, new Thing());
Spec.For(objects, s => true).QuickCheck(); // pass

2 个答案:

答案 0 :(得分:4)

我试图深入研究一下,看来你在FsCheck中发现了一个错误。

问题出现在文件Arbitrary.fs中,并且实际上只与字符串相关。我不得不替换它,他们在字符串上调用ToCharArray

    static member String() = 
        { new Arbitrary<string>() with
            override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate
            override x.Shrinker s = s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars))
        }

用这个

    static member String() = 
        { new Arbitrary<string>() with
            override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate
            override x.Shrinker s = 
                match s with
                    | null  -> seq {yield null;}
                    | _ -> s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars))
        }

您可能希望使用fscheck开发人员here来提高这一点,并检查我的修复是否正常 - 可能有更好的方法来实现它,但对于已经知道代码的人来说会更简单。

答案 1 :(得分:1)

对于FsCheck 1.x,我找到了一个涉及修改默认随机字符串生成器的解决方案:

public class MyArbitraries
{
    public static Arbitrary<string> String()
    {
        var nulls = Any.Value<string>(null);
        var nonnulls = Arb.Default.String().Generator;
        return Any.GeneratorIn(nulls, nonnulls).ToArbitrary;
    }
}

然后用以下内容初始化:

DefaultArbitraries.Add<MyArbitraries>();

然后问题中的测试按预期失败:

Spec.ForAny<string>(s => s != null).QuickCheck() // now fails, which is good

这将产生大约50%的空值和50%的随机字符串,权重可以调整:

Spec.ForAny<string>(s => true)
    .Classify(s => s==null, "null")
    .Classify(s => s!=null, "not null")
    .QuickCheck(); // displays percentages

但是,如果默认情况下不包含null值的决定是故意的而不是库中的错误,那么有效地覆盖默认字符串生成器可能不是一个好主意。并且,如果它是一个错误,它会在修复时扭曲分布。