Any suggestions for improvement on this style for BDD/TDD?
I was tinkering with doing the setups with our unit test specifciations which go like
Specification for SUT when behaviour X happens in scenario Y Given that this thing And also this other thing When I do X... Then It should do ... And It should also do ...
I wrapped each of the steps of the GivenThat in Actions... any feed back whether separating with Actions is good / bad / or better way to make the GivenThat clear?
/// <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,
ProductT开发者_如何学编程ype =
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();
}
Put them in separate methods so that you can compose them in other givens. Also, use underscores to replace spaces (instead of camel case). Also, create a method Given_that
that takes params of Action
delegates.
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);
That being said, I think the naming of your preconditions are not BDD. They are very technical in nature and do not denote the business need. If you were to tell a non-programmer what you were testing, you would probably not say "the product is stubbed for injection." You would more likely say
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
Now you can compose your "given" methods with little duplication:
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);
Composing your Givens, Whens and Thens in separate methods is a good idea and it's the way for instance SpecFlow (http://www.specflow.org) does it. So if rather want some automation for creating that boring piece of repetetive plumbing, I would really recomend using a tool like SpecFlow. And as a bonus you get a nice reporting tool :)
An other option to make your code a bit more fluent, is to make a little BDD base class. Take a look at Jonas Follesoe's brilliant little BDD DSL up at GitHub: 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; } }
}
And as Michael Meadows point out in his great answer; If you're going the BDD way of doing TDD (which you really should), keep focus on making your spesifications readable to business people. That means; stay away from technical wordings mock, inject, factory, exception, etc.
精彩评论