9

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

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

此外,似乎没有按设计处理空字符串,但我没有设法从文档中确定它。例如,仅在两个字符串(其中一个为空)之间进行选择是行不通的:

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
4

2 回答 2

5

我试图对此进行深入研究,您似乎在 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 开发人员提出此问题,并检查我的修复是否正常工作 - 可能有更好的方法来实现它,但对于已经知道代码的人来说会更简单。

于 2014-04-23T09:08:00.663 回答
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 值的决定是故意的,而不是库中的错误,那么有效地覆盖默认字符串生成器可能不是一个好主意。而且,如果它是一个错误,它会在修复后扭曲分布。

于 2014-04-23T11:16:05.387 回答