Fixture Object by Mark Seemann
(A Zero-Friction TDD post)
For a simple API, setting up the Fixture may be as simple as creating a new instance of the SUT, and possibly any Expected or Anonymous Variables. On the other hand, for a complex API, setting up the fixture may require quite a bit of (potentially repetitive) code.
Since the DRY principle also applies to test code, it quickly becomes necessary to create test-specific helper methods and other SUT API Encapsulation, and I've found that instead of creating a more or less unplanned set of disconnected helper methods, it's much cleaner (and, not to mention, much more object-oriented) to create a single object that represents the Fixture, and attach the helper methods to this object.
Let's look at an example.
Here's a unit test with a complex Fixture Setup:
[TestMethod] public void NumberSumIsCorrect_Naïve() { // Fixture setup Thing thing1 = new Thing() { Number = 3, Text = "Anonymous text 1" }; Thing thing2 = new Thing() { Number = 6, Text = "Anonymous text 2" }; Thing thing3 = new Thing() { Number = 1, Text = "Anonymous text 3" }; int expectedSum = new[] { thing1, thing2, thing3 }. Select(t => t.Number).Sum(); IMyInterface fake = new FakeMyInterface(); fake.AddThing(thing1); fake.AddThing(thing2); fake.AddThing(thing3); MyClass sut = new MyClass(fake); // Exercise system int result = sut.CalculateSumOfThings(); // Verify outcome Assert.AreEqual<int>(expectedSum, result, "Sum of things"); // Teardown }
If this was a truly one-off test and you know with certainty that there's going to be no other tests just remotely similar to this one, just hard-coding the entire Fixture Setup inline is in order, but as soon as the need for similar tests arises, this approach leads to repetitive code, and hence unmaintainable tests.
The more repetitive code that can be delegated to helper methods the better. A common refactoring of the previous test might then look something like this:
[TestMethod] public void NumberSumIsCorrect_Helpers() { // Fixture setup Thing thing1 = MyClassTest.CreateAnonymousThing(); Thing thing2 = MyClassTest.CreateAnonymousThing(); Thing thing3 = MyClassTest.CreateAnonymousThing(); Thing[] things = new[] { thing1, thing2, thing3 }; int expectedSum = things.Select(t => t.Number).Sum(); IMyInterface fake = new FakeMyInterface(); MyClassTest.AddThingsToMyInterface(fake, things); MyClass sut = new MyClass(fake); // Exercise system int result = sut.CalculateSumOfThings(); // Verify outcome Assert.AreEqual<int>(expectedSum, result, "Sum of things"); // Teardown }
While this is better, the helper methods are static methods, so it's necessary to pass too much state around. The array of Things and the fake is both needed in the test itself, as well as in the AddThingsToMyInterface helper method.
By moving and refactoring the helper methods to a new class that represents the Fixture, the test code becomes both more reusable and more readable.
[TestMethod] public void NumberSumIsCorrect_FixtureObject() { // Fixture setup MyClassFixture fixture = new MyClassFixture(); fixture.AddAnonymousThings(); int expectedSum = fixture.Things.Select(t => t.Number).Sum(); MyClass sut = fixture.CreateSut(); // Exercise system int result = sut.CalculateSumOfThings(); // Verify outcome Assert.AreEqual<int>(expectedSum, result, "Sum of things"); // Teardown }
The MyClassFixture instance now holds the state of the Fixture, so there's much less need to pass around as much data as before. The set of Things is now contained within the Fixture object itself, and the fake has totally disappeared from the test; it's still present, but now encapsulated within MyClassFixture.
internal class MyClassFixture { public MyClassFixture() { this.Fake = new FakeMyInterface(); this.Things = new List<Thing>(); } internal FakeMyInterface Fake { get; private set; } internal IList<Thing> Things { get; private set; } internal void AddAnonymousThings() { int many = 3; for (int i = 0; i < many; i++) { Thing t = this.CreateAnonymousThing(); this.Things.Add(t); this.Fake.AddThing(t); } } internal MyClass CreateSut() { return new MyClass(this.Fake); } private Thing CreateAnonymousThing() { Thing t = new Thing(); t.Number = this.Things.Count + 1; t.Text = Guid.NewGuid().ToString(); return t; } }
The CreateAnonymousThing method uses Constrained Non-Determinism to create unique Thing instances. The AddAnonymousThings method uses 3 as an equivalence of many, and the CreateSut method acts as a SUT Factory.
This is both more reusable and more expressive than a collection of disjointed static helper methods.
Whenever I begin to feel that setting up a Test Fixture is becoming too cumbersome, Fixture Object is the first pattern I consider.