在FsCheck中生成唯一字符串

3

我需要生成唯一的非null字符串,用作Dictionary键。我尝试了以下代码:

 public static Gen<NonNull<string>> UniqueStrings()
 {
     return from s in Arb.Default.NonNull<string>().Generator
            select s;
 }

然后我在以下代码中使用UniqueString()

public static Arb<Foo> Foos()
{
    // Foo's constructor will use the string parameter
    // as key to an internal Dictionary
    return (from nonNullString in UniqueStrings()
            select new Foo(nonNullString.Item)).ToArbitrary();
}

然而,在属性测试 Foo 中,我遇到了一个异常,因为 FsCheck 有时会生成相同的字符串,导致出现 DuplicateKeyException

我该如何生成唯一的字符串来传递给 Foo 的构造函数呢?


我有遗漏什么吗?基于 GUID 的 Guid.NewGuid().ToString(); 将生成唯一的字符串。 - Dmitry Bychenko
3个回答

3

你不能强制FsCheck生成器生成唯一值,因为你基本上无法访问先前生成的值的历史记录,也不会有FsCheck自己保证唯一性。

在这种情况下,你可以生成一个字符串列表,然后使用Distinct()来使列表唯一。你还可以使用类似的方法生成Foo列表。

例如:

Gen<Foo[]> res = from s in Arb.Default.Set<string>().Generator
                 select s.Select(ss => new Foo(ss)).ToArray();

(请注意,您不能使用 from 获取 ss 的值,因为 C# 不允许混合使用不同的 LINQ 方法,一个是在 Gen 上,一个是在 IEnumerable 上)
顺便问一下,我想知道这是否是您想要检查的额外属性。如果 Foo 的用户应该给它一个唯一的字符串,那么如何支持呢?如果他们没有提供怎么办?

你介意展示一下如何将字符串列表去重后传递给“Foo”的构造函数吗?我尝试了类似于“from ss in Gen.Default.Set<NonNull<string>>().Generator from s in ss select new Foo(s)”这样的代码,但是编译错误提示类型“Gen”没有“SelectMany”。 - rexcfnghk
我指的是 Arb.Default - rexcfnghk
Foo 的构造函数将把 string 插入到内部的 Dictionary 中,因此如果 string 参数不唯一,它将会抛出异常。在我看来,这不是一个很好的设计,因为前置条件非常不清晰,但我别无选择,只能坚持使用这个实现。我所能做的就是生成更多的测试,希望揭示其缺陷。 - rexcfnghk

2
生成唯一字符串可以使用Guid生成器,这是生成唯一字符串的标准方式,即使在多台计算机上也是如此。

虽然 GUID 是唯一的,但它们也具有相同的长度和字符集,这可能足以测试具有多样化输入集(如 null 值、长字符串、特殊字符等)的算法,也可能不足够。 - Stefano Ricciardi

0

不必生成唯一的字符串,您可以在将其插入字典之前添加一个简单的检查。

更新: 好的。在生成后对字符串进行洗牌。您可以在这里阅读相关内容。
虽然它是关于整数数组的,但您可以轻松地将其定制为字符串。


我正在尽力不修改Foo的实现,因为我只想防止FsCheck生成相同的string键。修改Foo的实现就像回避问题而不是解决问题。 - rexcfnghk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接