【问题标题】:How to generate null strings for FsCheck tests如何为 FsCheck 测试生成空字符串
【发布时间】:2015-10-13 22:41:17
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 如果它是一个错误,那么如果修复它可能会造成麻烦。所以他们可能会将其升级为“功能”。
    • @user2046431 我强烈怀疑它,因为这种情况将失败并且仅适用于 null string。即使他们决定将其升级为一项功能,他们至少可能希望在其网站的某个位置对其进行跟踪。
    • 同意这应该转到他们的问题跟踪器,并且修复收缩器使其不会阻塞空值不会破坏任何东西。我在想默认情况下不生成空值的“错误”。
    • 你为什么不提交一个拉取请求。当然,我会接受收缩机修复。我不太确定生成空值; FsCheck 的主要目标历史上一直是 F#,通常空值在那里不是什么大问题。
    • 请注意,在 FsCheck 2.x 中,正在生成空值并正确缩小字符串。
    【解决方案2】:

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

    【讨论】:

    • 我找不到 DefaultArbitraries 那家伙在哪里?
    • 此答案仅适用于 FsCheck 1.x。在 2.x 中,您应该改用 Arb.register。此外,在 2.x 中,会为字符串生成空值。
    猜你喜欢
    • 2016-04-20
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-07
    • 2018-03-31
    • 1970-01-01
    • 2018-08-10
    相关资源
    最近更新 更多