Building real test data

Battersea Power Station

Starting a blog is an interesting process – this will be my fourth post and I’m aware that blogging on a regular basis can be difficult – life (and work) have a habit of getting in the way. There are so many topics that I want to talk more extensively on, but I also want to get into the habit of putting out regular posts as well.

To that end today I’m going to briefly talk about some code that I wrote last week to help our team produce better unit and integration tests. I’ve previously blogged about generating large test data sets, but sometimes we need to build much simpler test data.

There are a number of frameworks out there that will build dummy test data(such as NBuilder), but sometimes as developers we need to get test data that is closer to “real” data. To that end I built this fairly simple framework that will generate c# code from an instance of a class. Within our solution we are using NHibernate and Sharp-Architecture, so I’ve simply extended our repositories so that I can build data directly from our database:

public static string GenerateTestData<T>(this IRepository<T> repository, params int[] ids) where T : DatabaseEntity
{
	var type = typeof(T);
	var code = new StringBuilder();
	var results = ids.Select(repository.Get);
 
	code.AppendLine(String.Format("var {0}s = new List<{1}>();", type.Name.ToLower(), type.Name));
	code.AppendLine("");
 
	foreach (var result in results)
	{
		code.AppendLine("// TEST ID: " + result.Id);
		code.AppendLine(String.Format("{0}s.Add({1});", type.Name.ToLower(), GenerateInstance(result)));
		code.AppendLine("");
	}
 
	return code.ToString();
}

This method simply gets all instances from the database that match the Id’s passed in via the method parameters. It produces a string of c# code, that will build a list containing all retrieved items. We generate the code for individual instances with this method:

private static string GenerateInstance<T>(T result, int indent = 0)
{
	var type = result.GetType();
	var data = new StringBuilder();
	data.AppendLine(String.Format("new {0}", type.Name));
	data.AppendLine(Tabs(indent) + "{");
 
	foreach (var property in type.GetProperties())
	{
		if (property.GetSetMethod() == null) continue;
 
		var value = WriteValue(property.GetValue(result), indent + 1);
		data.AppendLine(String.Format("{0}{1} = {2},", Tabs(indent + 1), property.Name, value));
	}
 
	data.Append(Tabs(indent) + "}");
	return data.ToString();
}
 
private static string Tabs(int n)
{
	return new String('\t', n);
}

Note that I’ve taken care to produce *nice* code, with correct indentation. The individual property values are generated with the below code, which also handles properties of other classes:

private static string WriteValue<T>(T value, int indent)
{
	if (value == null)
		return "null";
 
	if (value is decimal)
		return String.Format("{0}m", value);
 
	if (value is string)
		return String.Format("\"{0}\"", value);
 
	if (value is DateTime)
	{
		var date = Convert.ToDateTime(value);
		return String.Format("new DateTime({0}, {1}, {2}, {3}, {4}, {5})", date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second);
	}
 
	if (value is bool)
		return value.ToString().ToLower();
 
	if (value is Enum)
		return String.Format("{0}.{1}", value.GetType().Name, value);
 
	if (value.GetType().IsClass)
		return GenerateInstance(value, indent);
 
	return value.ToString();
}

This produces code similar to:

var records = new List<Record>();
 
// TEST ID: 54147
records.Add(new Record
{
	Reference = "ABC1234567",
	CreatedBy = "chris",
	CreatedDate = new DateTime(2014, 6, 14, 9, 30, 0),
	Person = new Person
	{
		PersonId = "ABCD1234",
		Title = "Mr",
		Name = "John",
		Surname = "Smith",
		Gender = Gender.Male,
		DateOfBirth = new DateTime(1937, 1, 1, 0, 0, 0),
	},
	Company = new Company
	{
		CompanyName = "ABC Company",
	},
        IsDeleted = false,
});

Photo Credit: Scott Wylie

Leave a Reply

Your email address will not be published.