【问题标题】:Any suggestions for improvement on this style for BDD/TDD?对于 BDD/TDD 这种风格的改进有什么建议吗?
【发布时间】:2010-05-21 16:48:18
【问题描述】:

我正在用我们的单元测试规范进行设置,就像

当行为 X 在场景 Y 中发生时的 SUT 规范 鉴于这件事 还有这另一件事 当我做X... 那么它应该做... 它也应该这样做......

我将 GivenThat 的每个步骤都包装在 Actions 中...任何反馈是否与 Actions 分开是好/坏/或更好的方式来明确 GivenThat?

        /// <summary>
        /// Given a product is setup for injection
        ///     And Product Image Factory Is Stubbed();
        ///     And Product Size Is Stubbed();
        ///     And Drawing Scale Is Stubbed();
        ///     And Product Type Is Stubbed();
        /// </summary>
        protected override void GivenThat()
        {
            base.GivenThat();

            Action givenThatAProductIsSetupforInjection = () =>
              {
                  var randomGenerator = new RandomGenerator();

                  this.Position = randomGenerator.Generate<Point>();

                  this.Product = new Diffuser
                                     {
                                         Size =
                                             new RectangularProductSize(
                                             2.Inches()),
                                         Position = this.Position,
                                         ProductType =
                                             Dep<IProductType>()
                                     };
              };

            Action andProductImageFactoryIsStubbed = () => Dep<IProductBitmapImageFactory>().Stub(f => f.GetInstance(Dep<IProductType>())).Return(ExpectedBitmapImage);

            Action andProductSizeIsStubbed = () =>
                 {
                     Stub<IDisplacementProduct, IProductSize>(p => p.Size);

                     var productBounds = new ProductBounds(Width.Feet(), Height.Feet());

                     Dep<IProductSize>().Stub(s => s.Bounds).Return(productBounds);
                 };

            Action andDrawingScaleIsStubbed = () => Dep<IDrawingScale>().Stub(s => s.PixelsPerFoot).Return(PixelsPerFoot);

            Action andProductTypeIsStubbed = () => Stub<IDisplacementProduct, IProductType>(p => p.ProductType);

            givenThatAProductIsSetupforInjection();
            andProductImageFactoryIsStubbed();
            andProductSizeIsStubbed();
            andDrawingScaleIsStubbed();
            andProductTypeIsStubbed();
        }

【问题讨论】:

    标签: c# unit-testing tdd bdd


    【解决方案1】:

    将它们放在单独的方法中,以便您可以在其他给定中组合它们。此外,使用下划线替换空格(而不是驼峰大小写)。另外,创建一个方法 Given_that 接受 Action 代表的参数。

    protected void Given_that(params Action[] preconditions)
    {
        foreach (var action in preconditions)
        {
            action();
        }
    }
    
    ...
    
    protected void a_product_is_set_up_for_injection()
    {
        ...
    }
    
    protected void product_image_factory_is_stubbed()
    {
        ...
    }
    
    etc...
    
    ...
    
    Given_that(a_product_is_set_up_for_injection,
               product_image_factory_is_stubbed,
               product_size_is_stubbed,
               drawing_scale_is_stubbed,
               product_type_is_stubbed);
    

    话虽如此,我认为您的先决条件的命名不是 BDD。它们本质上是非常技术性的,并不表示业务需求。如果您要告诉非程序员您正在测试什么,您可能不会说“产品已被存根注入”。你更可能会说

    Given a displacement product
        that is a two inch rectangular diffuser
        that has a random position
        that has a bitmap
        that has a size bounded by feet
        that has the expected pixels per foot
    

    现在你可以编写你的“给定”方法,几乎​​没有重复:

    protected [the type of your test class] Given(params Action given)
    {
        given();
        return this;
    }
    
    protected void That(params Action[] preconditions)
    {
        foreach (var precondition in preconditions)
        {
            precondition();
        }
    }
    
    Given(a_displacement_product)
        .That(is_a_two_inch_rectangular_diffuser,
              has_a_random_position,
              has_a_bitmap,
              has_a_size_bounded_by_feet,
              has_the_expected_pixels_per_foot);
    

    【讨论】:

    • 非常棒,我会用这个。并感谢您对命名条件的更正,应该为业务需要编写它是有道理的。
    【解决方案2】:

    在单独的方法中编写您的 GivensWhensThens 是个好主意,例如 SpecFlow strong> (http://www.specflow.org) 做到了。因此,如果想要一些自动化来创建那个无聊的重复管道,我真的建议使用像 SpecFlow 这样的工具。作为奖励,你会得到一个很好的报告工具:)

    另一个使您的代码更流畅的选择是创建一个小的 BDD 基类。在 GitHub 上查看 Jonas Follesoe 的精彩小 BDD DSL:http://gist.github.com/406014;

    public abstract class BDD<T> where T : BDD<T>
    {
        protected T Given { get { return (T)this; } }
        protected T And { get { return (T)this; } }
        protected T When { get { return (T)this; } }
        protected T Then { get { return (T)this; } }
    }
    

    正如迈克尔·梅多斯(Michael Meadows)在他的出色回答中指出的那样;如果您采用 BDD 方式进行 TDD(您确实应该这样做),请继续专注于使您的规范对业务人员具有可读性。这意味着;远离技术术语 mock、inject、factory、exception 等。

    【讨论】:

    • 邪恶!谢谢 Kjetil,我也会消化这个。
    猜你喜欢
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    相关资源
    最近更新 更多