首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在FsCheck中生成“复杂”对象?

如何在FsCheck中生成“复杂”对象?
EN

Stack Overflow用户
提问于 2014-02-24 12:39:23
回答 2查看 2.9K关注 0票数 10

我想要创建一个FsCheck发生器来生成一个“复杂”对象的实例。所谓复杂,我指的是C#中有许多子属性和集合的现有类。反过来,这些属性和集合需要为它们生成数据。

假设这个类名为Menu,带有子集合、DishesDrinks (我正在编造这些,所以忽略了糟糕的设计)。我想做以下几点:

  • 生成可变数量的Dishes和可变数量的Drinks
  • 使用Dish API生成Drink和FsCheck实例,以填充它们的属性。
  • 使用Menu API在FsCheck实例上设置一些其他基本属性。

如何为这种类型的实例编写生成器?这主意不好吗?(我刚开始进行基于属性的测试)。我读过这些文档,但到目前为止,我显然还没有把它全部内化。

有一个不错的https://stackoverflow.com/questions/8160421/in-fscheck-how-to-generate-a-test-record-with-non-negative-fields,但实际上只生成了3个相同类型的float__值。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-02-25 16:08:07

这不是个坏主意--事实上,这就是你能做到的全部。FsCheck的发电机是完全合成的。

首先要注意的是,如果您有不可变的对象,其构造函数采用原始类型,比如Dish和Dish,那么FsCheck可以立即生成这些对象(使用反射)。

代码语言:javascript
复制
let drinkArb = Arb.from<Drink>
let dishArb = Arb.from<Dish>

应该为您提供一个任意的实例,它是一个生成器(生成一个随机的饮料实例)和一个收缩器(使用一个饮料实例并使其“更小”)--这有助于调试,尤其是调试。对于复合结构,如果测试失败,就会得到一个小的反例)。

但是,这种情况很快就会分解--在您的示例中,您可能不需要饮料数量或菜肴数量的负整数。不过,上面的代码将生成负数。有时,如果您的类型实际上只是另一种类型的包装器,使用Arb.convert,这是很容易修复的。

代码语言:javascript
复制
let drinksArb = Arb.Default.PositiveInt() |> Arb.convert (fun positive -> new Drinks(positive) (fun drinks -> drinks.Amount)

您需要为饮料提供来回转换到Arb.convert和presto的新的任意实例,以保持您的不变状态。当然,其他不变量的维护也不是那么容易。

在此之后,从这两个部件同时生成一个生成器和一个收缩器就变得有点困难了。总是从生成器开始,如果你需要的话,收缩器会在稍后出现。@simonhdickson的例子看起来是合理的。如果有上面的任意实例,可以通过调用.Generator来获取它们的生成器。

代码语言:javascript
复制
let drinksGen = drinksArb.Generator

一旦您拥有了部件生成器(Dish和Dish),您确实可以像@simonhdickson建议的那样将它们组合在一起:

代码语言:javascript
复制
let menuGenerator =
    Gen.map3 (fun a b c -> Menu(a,b,c)) (Gen.listOf dishGenerator) (Gen.listOf drinkGenerator) (Arb.generate<int>)

分而治之!总的来说,看看Gen上的intellisense给您提供了一些关于如何组合生成器的想法。

票数 7
EN

Stack Overflow用户

发布于 2014-02-24 13:42:45

也许有一个更好的方法来描述这一点,但我认为这可能会做你想做的事情。每个Drink/Dish类型都可以使用与menuGenerator相同的样式来获取进一步的参数

代码语言:javascript
复制
type Drink() =
    member m.X = 1

type Dish() =
    member m.Y = 2

type Menu(dishes:Dish list, drinks:Drink list, total:int) =
    member m.Dishes = dishes
    member m.Drinks = drinks
    member m.Total = total

let drinkGenerator = Arb.generate<unit> |> Gen.map (fun () -> Drink())
let dishGenerator = Arb.generate<unit> |> Gen.map (fun () -> Dish())
let menuGenerator =
    Gen.map3 (fun a b c -> Menu(a,b,c)) <| Gen.listOf dishGenerator <| Gen.listOf drinkGenerator <| Arb.generate<int>
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21988046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档