Background
During the last few years automated acceptance tests and unit tests have become a very common scenario specially if you are developing software using Agile methodology. There are so many tools and utilities available which help us in getting better. They concentrate on very minor aspect but are very flexible and customizable. Two such tools or utilities which I found very useful in my project are Automapper and NBuilder. In this post I’ll demonstrate how we can use NBuilder to our advantage.
How to use NBuilder to generate Test data
NBuilder follows the recent trend of offering a Fluent Interface to work with its methods. NBuilder helps us to automatically assign values to the public properties of any class. These properties should be of intrinsic data types supported by DotNet framework. For the purpose of this demo I’ll create a entity class called Person having properties like FirstName, LastName, Gender etc. as shown below
public class PersonEntity
{
public int ID { get; set; }
public string FirstName { get; set;}
public string LastName { get; set;}
public DateTime DateOfBirth { get; set; }
public char Gender { get; set; }
}
I’ll create a small repository to manage the persons as shown below
public class PersonRepository
{
private IList<PersonEntity> _persons;
public IList<PersonEntity> Persons
{
get { return _persons;}
set { _persons = value;}
}
public IList<PersonEntity> GetAll()
{
return _persons;
}
public PersonEntity Get(int id)
{
return _persons.Single(p => p.ID == id);
}
public IList<PersonEntity> GetByGender(char gender)
{
return _persons.Where(p => p.Gender == gender).ToList();
}
}
This repository has methods for fetching all the persons, a single person based on the ID and a list of persons based on the gender. I have exposed the property to get and set the persons. Ideally this would be managed internally by the repository by means of a dependency in the form of a service or some other means. I don’t want to get into the burden of mocking the service calls. My intention is to concentrate on the NBuilder functionality and hence I’ll be managing the lists for time being.
So lets start by testing our GetAll method on the repository. In an ideal world, the repository will populate the internal data structure by means of making a service call or a data access call. But for the purpose of simplicity I’ll use the property to set this data for the time being. For these tests I would like to generate a list of 10 persons without really worrying about the values of each property. All I want to check is that if the repository contains 10 persons, it returns all 10 on call to GetAll or not. Here is my test
public class PersonRepositoryTest
{
private PersonRepository _personRepository;
[TestInitialize]
public void SetUp()
{
_personRepository = new PersonRepository();
}
[TestMethod]
public void GetAllTest()
{
const int numberOfPersons = 10;
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.Build();
_personRepository.Persons = persons.ToList();
IList<PersonEntity> result = _personRepository.GetAll();
Assert.AreEqual(numberOfPersons, result.Count, "Expected and actual count is not same");
}
}
The line of interest here is
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.Build();
Here I am using the Builder class and CreateListOfSize method by passing in the number of elements. I assign this to the Persons property on the PersonRepository object and call the GetAll method which returns me all the persons in the list.
If I were to do the same using traditional methods, I would have to create a method which would return me a list of persons by looping over a counter. Using NBuilder I can do that in a single line of code.
Lets look at the other method which filters the persons based on the ID. Here is the test for that
[TestMethod]
public void GetTest()
{
//Arrange
const int personID = 101;
const int numberOfPersons = 10;
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.WhereTheFirst(1)
.Have(p => p.ID = personID)
.Build();
_personRepository.Persons = persons.ToList();
//Act
PersonEntity result = _personRepository.Get(personID);
//Assert
Assert.AreEqual(personID, result.ID, "Expected and actual ID is not same");
}
Again I am creating the list of 10 persons. But the difference here is that I specifically instruct NBuilder to assign the ID value as 101 for the first element in the list. This is achieved through
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.WhereTheFirst(1)
.Have(p => p.ID = personID)
.Build();
You can also try other method which works with the position of the element in the collection like WhereTheLast method to achieve similar result.
If you have a big collection and you randomly want to assign a value to a field you can use the following approach
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.WhereRandom(1)
.Have(p => p.ID = personID)
.Build();
Lets use this approach to test the other method that we have on the repository for returning the list of persons based on the gender.
[TestMethod]
public void GetByGenderForMaleTest()
{
//Arrange
const char gender = 'M';
const int numberOfMales = 5;
const int numberOfPersons = 10;
IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
.WhereRandom(numberOfMales)
.Have(p => p.Gender = gender)
.Build();
_personRepository.Persons = persons.ToList();
//Act
IList<PersonEntity> result = _personRepository.GetByGender(gender);
//Assert
Assert.AreEqual(numberOfMales, result.Count, "Expected and actual number of males is not same");
}
Here I am saying randomly assign gender as ‘M’ for 5 items out of the 10 from the list. I can verify the same for by changing the gender to ‘F’.
Conclusion
As seen from the examples NBuilder is a very simple tool for building test data. It can save a lot of time and provides type safety which improves the quality of the test code. What I have shown here is very minimal features of NBuilder. In some future post I would like to concentrate more on advanced features. As always there might be better tools available to do similar things and I would like to hear and learn about them.
The complete source code along with the solution is available for download at http://dl.dropbox.com/u/1569964/NBuilderTest.zip.
Unit next time Happy Programming :)