On our last project we ended up with a shared test fixture for our unit tests which gave a lot of problems. So on our current project I've looked into the builder pattern. We run our unit tests in memory on the development machines and against the database on the build server.
Currently I have a T4 template which produces for example the following builder for a Student:
public class StudentBuilder : Builder<Student, StudentBuilder>
{
public StudentBuilder()
{
IsMale = true;
}
public StudentBuilder WithFirstName(string firstName)
{
this.FirstName = firstName;
return this;
}
public StudentBuilder WithLastName(string lastName)
{
this.LastName = lastName;
return this;
}
public StudentBuilder WithIsMale(bool isMale)
{
this.IsMale = isMale;
return this;
}
internal override Student Construct()
{
Student result = new Student()
{
FirstName = FirstName ?? "FirstName:" + id.ToString(),
LastName = LastName ?? "LastName:" + id.ToString(),
IsMale = IsMale,
Id = id,
};
/ return result;
}
}
Trough the base classes I can use this in the following way:
Student wouter = StudentBuilder.Build()
.WithFirstName("Wouter")
.WithLastName("de Kort");
List<Student> students = StudentBuilder.Build().Multiple(10, (builder, index) => builder.WithFirstName("FirstName" + index));
We run integration tests on our build server to make sure everything works against the database. This means we have to make sure that all referential constrains are met. But then the problems begin.
For example, a student is required to have a mentor, a mentor belongs to a school, a school to a city, a city to a ....
This would result in code like:
StudentBuilder.Build().WithMentor(MentorBuilder.Build().WithSchool(SchoolBuilder.Build().WithCity(CityBuilder.Build()))
How should I optimize this? I've thought about doing the 'default building' in the Construct method of each Builder but if I would build 10 students then it would lead to 10 mentors in 10 schools in 10 cities in 10....
Or maybe creating methods like WithAllCity(..), WithAll(School)
Any ideas? Am I actually using the Builder Pattern the right way? Could a Director class help? Or should I have inherited classes from StudentBuilder which solve these different cases?
Or another idea, should I add more validation in my service layer before sending the data to the database? Then I would catch more errors in my unit tests against the in memory database.