Geeks With Blogs

News INETA Community Speakers Program
GeeksWithBlogs.net: WTFNext's hosting!

View Stacy Vicknair's profile on LinkedIn


WTF Next? Dev ramblings from a master of nothing.

Test Data Builder Pattern

When unit testing we often need to fill any POCO objects with enough data to satisfy the needs of the test. Redoing this in every test can quickly become a burden and slow down the testing process. Test Data Builder is a pattern that’s meant to help in just that situation.

With test data builder you build a fluent interface that allows you to build out your POCO objects by only replacing the properties that you need specific values for. The other values are set to defaults specified by the builder.

Here’s a classic example of test data builder:

Our POCO Object

   1: Public Class Person
   2:     Public Property FirstName As String
   3:     Public Property LastName As String
   4:     Public Property DateOfBirth As DateTime
   5: End Class

Our Test Data Builder Class

   1: Public Class PersonBuilder
   2:     'Set the defaults
   3:     Private _firstName As String = "Default"
   4:     Private _lastName As String = "Default"
   5:     Private _dateOfBirth As DateTime = New DateTime(1999, 12, 31)
   6:  
   7:     Public Function WithFirstName(ByVal firstName As String) As PersonBuilder
   8:         _firstName = firstName
   9:         Return Me
  10:     End Function
  11:  
  12:     Public Function WithLastName(ByVal lastName As String) As PersonBuilder
  13:         _lastName = lastName
  14:         Return Me
  15:     End Function
  16:  
  17:     Public Function WithDateOfBirth(ByVal dateOfBirth As DateTime) As PersonBuilder
  18:         _dateOfBirth = dateOfBirth
  19:         Return Me
  20:     End Function
  21:  
  22:     Public Function Build() As Person
  23:         Return New Person() With
  24:             {
  25:                 .FirstName = _firstName,
  26:                 .LastName = _lastName,
  27:                 .DateOfBirth = _dateOfBirth
  28:             }
  29:     End Function
  30: End Class

Usage example

   1: Dim person = New PersonBuilder() _
   2:     .WithFirstName("Stacy") _
   3:     .WithDateOfBirth(New DateTime(1984, 12, 6)) _
   4:     .Build()

Still a lot of work

Depending on how many objects you need to create builders for and how many properties each object has you can still be in for a lot of upfront work following the test data builder pattern to create that fluent experience.

 

Creating the builder pattern with object initializers

In .NET we have object initializers that can take much of this extra work out by providing an expressive method of setting the object properties. If possible, simply subclassing off of the POCO will prevent us from even needing to write out all of the properties, then we can set default values in the constructor.

Our Subclassed Test Data Builder

   1: Public Class PersonBuilderWithObjectInitializers
   2:     Inherits Person
   3:  
   4:     Public Sub New()
   5:         FirstName = "Default"
   6:         LastName = "Default"
   7:         DateOfBirth = New DateTime(1999, 12, 31)
   8:     End Sub
   9:  
  10:     Public Function Build() As Person
  11:         Return New Person() With
  12:             {
  13:                 .FirstName = FirstName,
  14:                 .LastName = LastName,
  15:                 .DateOfBirth = DateOfBirth
  16:             }
  17:     End Function
  18: End Class

The new builder’s usage

   1: Dim person = New PersonBuilderWithObjectInitializers() With
   2:         {
   3:             .FirstName = "Stacy",
   4:             .DateOfBirth = New DateTime(1984, 12, 6)
   5:         }.Build()

Why still have a build method?

Some of you might be asking, “Why keep the build method when polymorphism should do the work?” Well, good point. The reason I keep the build method is that when working against Entity Framework against POCO objects it will attempt to figure out the true type of the object, not just the type the object is cast as. As a result, it will complain when trying to persist the object. The build method in this case just acts as added assurance that if any type discovery is called against the object you will be safe knowing that the correct type will be discovered.

 

C# examples

Our POCO Object

   1: public class Person
   2: {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:     public DateTime DateOfBirth { get; set; }
   6: }

Our Test Data Builder Class

   1: public class PersonBuilder
   2: {
   3:     // Set our defaults
   4:     private string _firstName = "Default";
   5:     private string _lastName = "Default";
   6:     private DateTime _dateOfBirth = new DateTime(1999, 12, 31);
   7:  
   8:     public PersonBuilder WithFirstName(string firstName)
   9:     {
  10:         _firstName = firstName;
  11:         return this;
  12:     }
  13:  
  14:     public PersonBuilder WithLastName(string lastName)
  15:     {
  16:         _lastName = lastName;
  17:         return this;
  18:     }
  19:  
  20:     public PersonBuilder WithDateOfBirth(DateTime dateOfBirth)
  21:     {
  22:         _dateOfBirth = dateOfBirth;
  23:         return this;
  24:     }
  25:  
  26:     public Person Build()
  27:     {
  28:         return new Person
  29:                    {
  30:                        FirstName = _firstName,
  31:                        LastName = _lastName,
  32:                        DateOfBirth = _dateOfBirth
  33:                    };
  34:     }
  35: }

Usage example

   1: //arrange
   2: var person = new PersonBuilder()
   3:     .WithFirstName("Stacy")
   4:     .WithDateOfBirth(new DateTime(1984, 12, 6))
   5:     .Build();

Our Subclassed Test Data Builder

   1: public class PersonBuilderWithObjectInitializers : Person
   2: {
   3:     public PersonBuilderWithObjectInitializers()
   4:     {
   5:         // Set our defaults
   6:         FirstName = "Default";
   7:         LastName = "Default";
   8:         DateOfBirth = new DateTime(1999, 12, 31);
   9:     }
  10:  
  11:     public Person Build()
  12:     {
  13:         return new Person
  14:             {
  15:                 FirstName = FirstName,
  16:                 LastName = LastName,
  17:                 DateOfBirth = DateOfBirth
  18:             };
  19:     }
  20: }

The new builder’s usage

   1: var person = new PersonBuilderWithObjectInitializers
   2:                 {
   3:                     FirstName = "Stacy",
   4:                     DateOfBirth = new DateTime(1984, 12, 6)
   5:                 }.Build();
Posted on Sunday, October 9, 2011 6:59 AM VB .NET , C# .NET | Back to top


Comments on this post: Test Data Builder pattern with object initializers in .NET

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Stacy Vicknair | Powered by: GeeksWithBlogs.net