【问题标题】:DSL to generate test dataDSL生成测试数据
【发布时间】:2010-04-13 11:54:18
【问题描述】:

有几种方法可以为测试(不仅仅是单元测试)生成数据,例如 Object Mother、builder 等。另一种有用的方法是将测试数据编写为纯文本:

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)
  • 嘘(我认为可以做 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);

请注意,我首先创建子产品,然后将其添加到主产品,即使它以相反的顺序列出。价格以特殊方式处理。我想指定子产品的名称并获得对它的引用 - 创建。我想在单行上列出所有产品属性 - 平面和非重复。我想对属性使用默认值。以此类推。

更新:我不相信要避免使用 DSL,因为所有替代示例都过于冗长且对用户不友好。没有人说任何关于 DSL 的有用信息。

【问题讨论】:

    标签: unit-testing antlr dsl


    【解决方案1】:

    对于数据 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,并且对它很满意。

    但是,如果我们谈论的是 unit-tests,通常使用就地构造函数和属性分配“手动”构造必要的对象会更简单、更易读。这是因为单元测试本质上高度关注某些代码(单元),并且创建足够用于测试的数据基础设施应该不难。在单元测试中对半完整的实体进行操作是可以的,不要为构建与这个具体测试无关的数据而烦恼。

    对于功能测试,YAML 非常棒。

    【讨论】:

    • 它是多行的,在我看来使用起来更糟糕。
    • 单行仅在数据平坦的简单情况下保持可读。如何在单行中反映嵌套结构?
    • 简单的情况是做测试时的大多数情况(就像你说的那样);使用 DSL,我们可以为 80% 的简单案例做单线,而对于剩下的 20%,我们仍然可以做多线;使用多行,即使对于简单的情况,我们也不得不变得冗长。这对于将测试数据输入文本表的类似 FitNesse-Wiki 的系统尤其不利。
    【解决方案2】:

    我首先会看看我选择的语言是否足够丰富来构建我的 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”,解析器应该知道第一个是主要产品价格,第二个是维护产品价格。不用每次都写这些细节。
    • 如果我写“价格:145”,解析器应该足够聪明,可以将两个价格设置为相同的值。或者,也许,维护是半价。如果您添加此类详细信息,您将看到对 DSL 的需求。
    • @queen3:关于你的第一条评论,我同意; DSL 是否值得归结为您自己对线路噪声的痛苦阈值。但是,您的第二点是不正确的; TestProduct.prices 的设置器可以轻松查看是否提供了一个或两个价格,并相应地应用您选择的规则。
    • 不,它不能;我不会更改域类以适应测试需求。例如。域类有 AddPrice() 但没有 SetPrice(); AddPrice 不知道是 1 还是 2 价格。
    • TestProductProduct 的派生类。它应该能够以与您的 DSL 完全相同的方式填充其基类。
    【解决方案3】:

    对于创建外部 DSL,我推荐 Eclipse TMF Xtext,它非常好(基于 ANTLR,但更简单),但构建在 Eclipse 和 Java 之上,但是您可以生成任何代码。 在创建测试数据时,我受到 Ruby on Rails 人员的启发,这是另一个答案中提到的 YAML 固定装置,但我也看到了一种使用工厂的方法,它可以帮助您摆脱一些重复和不灵活。看看这个Railscasts 158: Factories not Fixtures,它可能会给你一些设计DSL的想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-27
      • 2013-02-27
      • 2023-03-30
      相关资源
      最近更新 更多