Unit testing all subclasses

This post will demonstrate a simple technique using Visual Studio and NUnit for writing a single unit test that tests all current and future implementations of a base class.

Background

My current project has an abstract class representing an email message; the abstract base contains the basics like ‘to’, ‘CC’, ‘subject’ and so on, and each type of email that the app sends is represented by a concrete class inheriting from the base. In a separate class library, we have a template engine which takes an email object, loads an appropriate template, and uses the awesome RazorEngine to build the email HTML for sending.

For example, we have PasswordResetEmail, which sends users a new password if they forgot theirs; this has UserName and NewPassword properties to be injected into the template.

Email inheritance diagram

For simplicity, the templates are stored as embedded resources in the assembly, so this entire process is eminently unit testable.

Testing all templates

I want to test that the template engine can successfully process each email object; there are a dozen or so of these, and we can envisage another dozen or so being added. Initially, we tested a couple of templates and then called it a day, but recently we found that we had Razor compilation errors in some of the untested ones.

We could have copy/pasted tests for each template, but:

  1. that violates the DRY principle
  2. it’s easy to forget to test new templates
  3. frankly, it just looks messy

Instead, here’s what we wrote:

public static EmailBase[] EmailObjects
{
	get
	{
		var objects = new List();
		
		// get all inheriting types
		var types = typeof(EmailBase).Assembly
			.GetTypes()
			.Where(type => type.IsSubclassOf(typeof(EmailBase)));

		foreach (var type in types)
		{
			// get the default constructor
			var ctor = type.GetConstructor(new Type[] { });
			if (ctor != null)
			{
				objects.Add((EmailBase)ctor.Invoke(new object[] { }));
			}
		}

		return objects.ToArray();
	}
}

[Test]
[TestCaseSource("EmailObjects")]
public void RazorTemplateEngine_RenderHtmlTest(EmailBase model)
{
	ITemplateEngine target = new RazorTemplateEngine();
	string html = target.RenderHtml(model);
	Assert.That(HtmlIsValid(html)); // details excluded for brevity
}

Et voila! With a simple bit of reflection and NUnit’s TestCaseSource attribute, we can automatically pass in all classes which inherit from EmailBase and test that our template engine can render their HTML successfully! If we think of new tests to add, we can use the same technique to apply them to all EmailBase objects in the codebase.

Final thoughts

Unit testing (or TDD if you like) is hard. We’ve noticed a tendency to disregard the normal rules of DRY, SOLID etc when writing tests because they’re just test code – in reality, maintenance of tests is no less important than the maintenance of the code codebase (arguably more so). Always consider whether there’s a more elegant way of writing test code!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.