有关BDD / TDD这种风格的改进建议吗?

时间:2010-05-21 16:48:19

标签: c# unit-testing tdd bdd

我正在修改使用我们的单元测试规范进行设置,如

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

我在Actions中包含了GivenThat的每个步骤...任何反馈是否与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();
        }

2 个答案:

答案 0 :(得分:2)

将它们放在不同的方法中,以便您可以在其他方式中组合它们。另外,使用下划线代替空格(而不是驼峰)。另外,创建一个方法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);

答案 1 :(得分:1)

在单独的方法中编写 Givens Whens Thens 是一个好主意,它就像实例 SpecFlow 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; } }
}

正如迈克尔梅多斯在他的回答中指出的那样;如果您采用BDD方式进行TDD(您确实应该这样做),请继续专注于让您的商务人士可以阅读。那意味着;远离技术措辞模拟,注入,工厂,例外等。