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.
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:
- that violates the DRY principle
- it’s easy to forget to test new templates
- 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!