The given-expect testing pattern

25 Apr 2010

I was watching Brett Schuchert’s TDD screencast on implementing the shunting yard algorithm in C#. In it Brett builds up his tests in a style I hadn’t come across before. Each test is expressed as a given-expect statement. A pattern that is particularly useful in situations in which a class has a main method that accepts an open-ended number of dissimilar inputs.

I found the given-expect pattern useful in testing a piece of code that I was working on this week. I was refactoring and adding tests around an ASP.NET control adapter that makes SharePoint 2007 pages more XHTML compliant. I wanted to reuse the transformations outside the control adapter and hence ended up moving the transformation logic to a new class. It accepts possibly malformed HTML and relies on heuristics of the HTML Agility Pack to build a DOM off it. I can then query the DOM, looking for known violations, and patch them before returning XHTML to the caller.

public class HtmlToXHtmlTransformer {
    private readonly HtmlDocument _document;

    public HtmlToXHtmlTransformer(string html) {
        _document = new HtmlDocument();
        _document.DetectEncoding(new StringReader(html));
        _document.LoadHtml(html);
    }

    private void Transform(string xpath, Action<HtmlNode> nodeMatch) {
        var nodes = _document.DocumentNode.SelectNodes(xpath);
        if (nodes != null)
        foreach (var node in nodes)
            nodeMatch.Invoke(node);
    }

    private void FixDuplicateBorderAttributeOnSPGridViewControl() {
        Transform("//table[count(@border)=2]", node => node.Attributes.Remove("border"));
    }

    public string Transform() {
        FixDuplicateBorderAttributeOnSPGridViewControl();
        _document.OptionWriteEmptyNodes = true;
        return _document.DocumentNode.WriteTo();
    }
}

The complete HtmlToXHtmlTransformer collects a dozen transformations. Its Transform method is what we want to call with various HTML fragments to verify that they come out as XHTML. For this purpose, we might do the tests as Visual Studio data-driven tests that read their input and output from a text file. But in most cases I prefer traditional tests, so I can describe the purpose of a test with a descriptive method name and possibly a comment.

[TestClass]
public class HtmlToXHtmlTransformerTest {
    private string _result;

    [TestMethod]
    public void Must_selfclose_nodes_when_allowed() {
        Given("<br>");
        Expect("<br />");
    }

    [TestMethod]
    public void Must_remove_duplicate_border_on_SPGridView_control {
        Given(@"<table border=""0"" border=""0""></table>");
        Expect(@"<table border=""0""></table>");
    }

    private void Expect(string xhtml) {
        Assert.AreEqual(xhtml, _result);
    }

    private void Given(string html) {
        var transformer = new HtmlToXHtmlTransformer(html);
        _result = transformer.Transform();
    }
}

I particularly like the clarity of the given-expect pattern and find that for a reasonable number of tests it’s an alternative to data-driven test. I do, however, recognize the value of data-driven tests in situations where a non-developer wants to test a class. Though at the unit test level I’ve never experienced this. It’s more characteristic of FitNesse for acceptance testing. However you unit test, just make sure your tests run with a minimum of effort on your part and that they run fast.