Unit testing DSL生成测试数据

Unit testing DSL生成测试数据,unit-testing,antlr,dsl,Unit Testing,Antlr,Dsl,有几种方法可以为测试(不仅仅是单元测试)生成数据,例如对象母亲、构建器等。另一种有用的方法是以纯文本形式编写测试数据: 然后将其解析为C#对象。这在单元测试中很容易使用(因为深层的内部集合可以用单行编写),在类似FitNesse的系统中使用更方便(因为这个DSL自然适合wiki),等等 所以我使用它来编写解析器,但每次编写都很乏味。我不是DSL/语言解析器方面的专家,但我认为他们可以在这里提供帮助。什么是正确的选择?我只听说: DSL(我的意思是,任何DSL) Boo(我认为可以做DSL) A

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

然后将其解析为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);
请注意,我首先创建子产品,然后将其添加到main,即使它是按相反顺序列出的。价格是以特殊的方式处理的。我想指定子产品的名称并获取对它的引用-已创建。我想在一行上列出所有的产品属性——平直和不重复。我想对属性使用默认值。等等


更新:我不相信要避免DSL,因为所有的替代示例都太冗长且不友好。而且没有人对DSL说任何有用的话。

我首先要看看我选择的语言是否足够丰富,足以构建我的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可以进行翻译。

对于数据,DSL是一个很好的选择。以下是来自维基百科的示例:

---
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非常好。

对于创建外部DSL,我推荐EclipseTMF Xtext,它非常好(基于ANTLR但更简单),但构建在Eclipse和Java之上,但是您可以生成任何代码。
当谈到创建测试数据时,我受到RubyonRails人员的启发,这是另一个答案中提到的YAML fixture,但我也看到了一种使用工厂的方法,它可以帮助您摆脱一些重复性和僵化性。看看这个,它可能会给你一些设计DSL的想法。

它要详细得多,特别是如果你补充说价格不仅仅是整数,而且包含的不是字符串,而是对其他产品的引用(见更新)。我想写“prices:145255”,解析器应该知道第一个是主产品价格,第二个是维护产品价格。如果我写“prices:145”,解析器应该足够聪明,可以将两个价格设置为相同的值。或者,也许,维修费是半价。如果你加上这些细节,你就会看到DSL的必要性。@queen3:关于你的第一个评论,我同意;DSL是否值得,取决于你自己对线路噪音的痛楚。然而,你的第二点是不正确的;
TestProduct.prices
的setter可以轻松查看是否提供了一个或两个价格,并相应地应用您选择的规则。不,它不能;我不会改变域类来满足测试需要。例如,域类有AddPrice(),但没有SetPrices();AddPrice不知道是否有1个或2个价格。
TestProduct
Product
的派生类。它应该能够以与DSL完全相同的方式填充其基类。它是多行的,在我看来使用更糟糕。只有在数据平坦的简单情况下,单行才保持可读性。如何在单行中反映嵌套结构?在进行测试时,大多数情况下都是简单的情况(正如您所说);使用DSL,我们可以对80%的简单情况使用单线,对其余20%的情况使用多线;使用multiline,即使对于简单的情况,我们也不得不变得冗长。这对于FitNesse Wiki类系统来说尤其糟糕,在这些系统中,测试数据被输入到文本表中。
---
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.