Using the Test Data Builder Pattern in unit tests

In an application with lots of complex dependencies, writing unit tests that actually test a single unit of code is challenging. A customer has a large application where most of the "unit" tests are actually integration tests that rely on known test data in the database.

For new development in this application, I've been moving toward true unit tests where possible. Using mock objects helps. Instead of the external dependencies for the system under test relying on the database, I mock the dependencies with RhinoMocks.

Configuring the test data in the mocks is still painful, though, since each employee, company, etc. needs to be built in a valid state.

In the past I've used the Object Mother pattern, where a single class is responsible for creating and configuring objects needed in the course of a test. That approach brings its own pain, though, since each test requires the objects to be configured slightly differently.

For my latest work on this app, I've been using the Test Data Builder pattern, and have found it to be much easier to develop, maintain, and understand the tests.

http://geekswithblogs.net/Podwysocki/archive/2008/01/08/118362.aspx

With the Test Data Builder pattern, the class that's responsible for creating the test objects provides a series of methods that can be used to configure the test objects. Properties that aren't configured use default values that work in the "typical" test case. The builder methods can be chained together, making the test setup happen in a few easy-to-read lines of code.

Here's an example of a unit test that uses this pattern. The ExportItemTestBuilder class is responsible for building a valid export item. The test verifies that when the export is performed, the totals are copied to the exported item:

            [Test]

            public void HoursFromCategoryTotalsAreExported()

            {

                        ExportItemTestBuilder.Context context;

                        decimal categoryHours = 77.7m;

                        using (repository.Record())

                        {

                                    context = new ExportItemTestBuilder(repository)

                                                .WithCategoryHours(categoryHours)

                                                .WithPayType(PayType.Hourly)

                                                .Build();

                        }

 

                        using (repository.Playback())

                        {

                                    DynamicsGPExportItem item = new DynamicsGPExportItem(context.Exporter);

                                    item.AddTotals(context.Employee, context.WorkPeriod, context.Category,

                                    context.CategoryTotals, context.WorkPeriod);

 

                                    Assert.AreEqual(categoryHours, item.Items[0].TRXHRUNT);

 

                        }

            }

In this test, the test data builder is using defaults for everything except the category hours and pay type.


Feedback

No comments posted yet.


Post a comment





 

Please add 3 and 6 and type the answer here: