DSL 生成测试数据

5

有几种方法可以为测试(不仅限于单元测试)生成数据,例如对象工厂、构建器等。另一种有用的方法是将测试数据编写成纯文本:

product: Main; prices: 145, 255; Expire: 10-Apr-2011; qty: 2; includes: Sub
product: Sub; prices: 145, 255; Expire: 10-Apr-2011; qty: 2
然后将其解析为C#对象。这在单元测试中很容易使用(因为深度内部集合可以写成单行),在类似FitNesse的系统中使用更方便(因为这种DSL自然适合wiki),等等。
因此,我使用它并编写解析器,但每次编写都很繁琐。我不是DSL /语言解析器的大专家,但我认为它们可以在这里提供帮助。哪种才是正确的选择?我只听说过:
- DSL(我的意思是任何 DSL) - Boo(我认为可以做DSL) - ANTLR
但我甚至不知道该选择哪一个和从哪里开始。
所以问题是:使用某种DSL来生成测试数据是否合理?您建议怎么做?是否存在任何现有案例?
更新:似乎我没有表达清楚。这不是关于原始字符串到对象转换的问题。请看第一行并将其与下面的内容联系起来。
var main = Product.New("Main")
   .AddPrice(Price.New(145).WithType(PriceType.Main).AndQty(2))
   .AddPrice(Price.New(255).WithType(PriceType.Maintenance).AndQty(2))
   .Expiration(new DateTime(10, 04, 2011));
var sub =  Product
   .New("Sub").Parent(main)
   .AddPrice(...));
main.AddSubProduct(sub);
products.Add(main);
products.Add(sub);

请注意,我首先创建子产品,然后将其添加到主产品中,即使它以相反的顺序列出。价格以一种特殊的方式处理。我想指定子产品的名称并获取对其的引用-已创建。我想在单行上列出所有产品属性-FLAT和NON-REPEATATIVE。我想使用属性的默认值。等等。
更新:我不确定是否应避免DSL,因为所有替代示例都太冗长且不用户友好。没有人对DSL提供有用的信息。
3个回答

2
对于数据DSL,YAML是一个很好的选择。以下是维基百科上的一个示例:
---
receipt:     Oz-Ware Purchase Invoice
date:        2007-08-06
customer:
    given:   Dorothy
    family:  Gale

items:
    - part_no:   A4786
      descrip:   Water Bucket (Filled)
      price:     1.47
      quantity:  4

    - part_no:   E1628
      descrip:   High Heeled "Ruby" Slippers
      price:     100.27
      quantity:  1

bill-to:  &id001
    street: |
            123 Tornado Alley
            Suite 16
    city:   East Westville
    state:  KS

ship-to:  *id001

specialDelivery:  >
    Follow the Yellow Brick
    Road to the Emerald City.
    Pay no attention to the
    man behind the curtain.

我在多个项目中使用了YAML,并对其感到满意。
但是,如果我们谈论的是单元测试,通常更简单易读的方法是使用构造函数和属性赋值来手动构建必要的对象。这是因为单元测试本质上高度聚焦于某些代码(单元),因此创建仅适用于测试的数据基础设施不应该很困难。在单元测试中操作半完成的实体是可以的,不必费心构建与此具体测试无关的数据。
对于功能测试,YAML非常好用。

1
它是多行的,依我的观点使用起来更糟糕。 - queen3
单行仅适用于数据是简单扁平的情况,如何在单行中反映嵌套结构? - nkrkv
简单情况是测试时的大多数情况(正如您所说);使用DSL,我们可以为80%的简单情况编写单行代码,而对于剩下的20%,我们仍然可以编写多行代码;对于简单情况,我们被迫使用多行代码来表达,这尤其对类似于FitNesse-Wiki的系统非常不利,因为测试数据是输入到文本表格中的。 - queen3

1

我首先会看看我选择的编程语言是否足够丰富,可以构建我的DSL。C#应该可以很容易地处理您的情况:

Product[] products = new Product[] {
    new TestProduct{product="Main", prices=new[]{145, 255}, Expire="10-Apr-2011", qty=2, includes="Sub"},
    new TestProduct{product="Sub", prices=new[]{145, 255}, Expire="10-Apr-2011", qty=2}
};

虽然不太美观,但足够容忍,以至于我会很难为自定义DSL的额外努力辩护。

还要注意的是,Expire使用字符串进行初始化,但显然它是一个日期。对于DSL习语来说,这是完全合理的,因为TestProduct.Expire的setter可以进行转换。


它更冗长,特别是如果你添加了价格不仅仅是整数,而且包括不是字符串而是其他产品的引用(请参见更新)。我想写“价格:145、255”,解析器应该知道第一个是主要产品价格,第二个是维护产品价格。而不是每次都写这些细节。 - queen3
如果我写下 "prices: 145",解析器应该足够聪明,将两个价格设置为相同的值。或者,也可以将维护费用降为半价。如果您添加这样的细节,您会发现需要使用领域特定语言(DSL)。 - queen3
@queen3:关于你的第一个评论,我同意;是否值得使用DSL取决于你对代码噪声的容忍度。然而,你的第二个观点是不正确的;TestProduct.prices的setter可以轻松地看到是否提供了一个或两个价格,并相应地应用你选择的规则。 - Marcelo Cantos
不行,我不会修改领域类以适应测试需求。例如,领域类有AddPrice()但没有SetPrices();AddPrice不知道是否有1个或2个价格。 - queen3
TestProductProduct的派生类。它应该能够以与您的DSL完全相同的方式填充其基类。 - Marcelo Cantos
显示剩余2条评论

0

如果要创建外部DSL,我建议使用Eclipse TMF Xtext,它非常好用(基于ANTLR但更简单),但是构建在Eclipse和Java之上,因此您可以生成任何代码。 在创建测试数据方面,我受到了Ruby on Rails的方式的启发,他们使用YAML fixtures,如另一个答案中所提到的,但我也看到了一种使用工厂的方法,这可以帮助您摆脱一些重复性和不灵活性。看看这个Railscasts 158: Factories not Fixtures,它可能会给您设计DSL的一些想法。


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