More about frozen pizza by Mark Seemann
In my previous blog post, I introduced AutoFixture's Freeze feature, but the example didn't fully demonstrate the power of the concept. In this blog post, we will turn up the heat on the frozen pizza a notch.
The following unit test exercises the BasketPresenter class, which is simply a wrapper around a Basket instance (we're doing a pizza online shop, if you were wondering). In true TDD style, I'll start with the unit test, but I'll post the BasketPresenter class later for reference.
[TestMethod] public void AddWillPipeMapCorrectly() { // Fixture setup var fixture = new Fixture(); var basket = fixture.Freeze<Basket>(); var mapMock = new Mock<IPizzaMap>(); fixture.Register(mapMock.Object); var pizza = fixture.CreateAnonymous<PizzaPresenter>(); var sut = fixture.CreateAnonymous<BasketPresenter>(); // Exercise system sut.Add(pizza); // Verify outcome mapMock.Verify(m => m.Pipe(pizza, basket.Add)); // Teardown }
The interesting thing in the above unit test is that we Freeze a Basket instance in the fixture. We do this because we know that the BasketPresenter somehow wraps a Basket instance, but we trust the Fixture class to figure it out for us. By telling the fixture instance to Freeze the Basket we know that it will reuse the same Basket instance throughout the entire test case. That includes the call to CreateAnonymous<BasketPresenter>.
This means that we can use the frozen basket instance in the Verify call because we know that the same instance will have been reused by the fixture, and thus wrapped by the SUT.
When you stop to think about this on a more theoretical level, it fortunately makes a lot of sense. AutoFixture's terminology is based upon the excellent book xUnit Test Patterns, and a Fixture instance pretty much corresponds to the concept of a Fixture. This means that freezing an instance simply means that a particular instance is constant throughout that particular fixture. Every time we ask for an instance of that class, we get back the same frozen instance.
In DI Container terminology, we just changed the Basket type's lifetime behavior from Transient to Singleton.
For reference, here's the BasketPresenter class we're testing:
public class BasketPresenter { private readonly Basket basket; private readonly IPizzaMap map; public BasketPresenter(Basket basket, IPizzaMap map) { if (basket == null) { throw new ArgumentNullException("basket"); } if (map == null) { throw new ArgumentNullException("map"); } this.basket = basket; this.map = map; } public void Add(PizzaPresenter presenter) { this.map.Pipe(presenter, this.basket.Add); } }
If you are wondering about why this is interesting at all, and why we don't just pass in a Basket through the BasketPresenter's constructor, it's because we are using AutoFixture as a SUT Factory. We want to be able to refactor BasketPresenter (and in this case particularly its constructor) without breaking a lot of existing tests. The level of indirection provided by AutoFixture gives us just that ability because we never directly invoke the constructor.
Coming up: more fun with the Freeze concept!
Comments
It's not clear from the example how using AutoFixture for DI keeps our tests testing object behavior better than setting up dependencies through the constructor or setters. I think it will suffer the same problems as we're examining private data. It has one benefit in that our public APIs remain pristine from adding in special-ctors/Setters for DI.
But hey, I'm going to play with this and see what happens. Thanks for the code sample!
Am I missing something?
==>Lancer---
http://ConfessionsOfAnAgileCoach.blogspot.com
[1]
[code]private readonly Basket basket;
private readonly IPizzaMap map;
public BasketPresenter(Basket basket, IPizzaMap map) // <-- redesign for DI
[/code]