Unit testing a Katmai Reporting Services renderer

I am in the process of documenting the way my Katmai unit tests work. There’s nothing inventive about them – they are straightforward and meant to get the job done. I use the VSTS unit runner which works though not particularly well. I do not go through the machinations of using and keeping up to date the VSTS method of accessing private/internal type members. Instead, I use partial classes as described below. I also use mock objects to make things easy that would otherwise be difficult to test, for instance doing away with having to maintain “baseline” TIFF files that describe the correct rendering of a given report. Attempting to maintain bit-perfect baselines is generally an exercise in frustration – mock objects are so much better in a majority of cases.

My two primary frustrations with unit testing using the built-in VSTS infrastructure is how slow the runner is and how painful it is to run a single test or group of tests. VSTS Orcas is making great headway on both of these issues and I’m willing to slog it out in VS2005 a while longer until Orcas RTMs.

The way the Katmai image-based renderers work is fairly straightforward. There is a Renderer class which walks a report layout tree and sends commands to a writer class. There are currently 3 writer classes in this assembly – one for compatibility with the WinForms control we shipped in Visual Studio 2005, one for PDF, and one for images (TIFF, EMF-printing, JPG, etc). The writer classes accept basic commands (start a new page, draw a line, write some text) and translates them into format-specific output.

Taking the following example ImageWriter class:

internal partial sealed class ImageWriter
{

    private Graphics m_graphics;

    internal void DrawString(...);
}

 

When the Renderer class comes to a textbox, it calls into ImageWriter.DrawString, passing in font, color, and text information. DrawString then calls into System.Drawing.Graphics.DrawString via the m_graphics member.

To test this interaction, since the class is internal to the ImageRenderer assembly, I need to add the source file containing the ImageWriter class directly into my VSTS test project.  Now I can write a test that mimics the Renderer call into the writer:

[TestMethod]

public void Test_DrawString()

{

    Renderer renderer = new Renderer();

    renderer.ImageWriter.DrawString(new Font("Arial", 10f), Color.Black,

        "Test String",

        new RectangleF(0f, 0f, 10f, 10f));

}

 

To verify that ImageWriter is indeed calling Graphics.DrawString, we can use a simple mock Graphics object.

internal sealed class Graphics
{

    internal Queue<MethodInvocation> MethodInvocations;

    ~Graphics()

    {

    Dispose();

    }

    public void Dispose()

    {

    if (MethodInvocations!= null)

    {

    Assert.AreEqual<Int32>(0, MethodInvocations.Count,

    "Not all expected methods were invoked");

    MethodInvocations = null;

    }

    }

    internal override void DrawString(Font font, Color color,

        String string, RectangleF position)

    {

    Assert.IsNotNull(MethodInvocations);

    MethodInvocation method = MethodInvocations.Dequeue();

    MethodInvocation.VerifyMethodInvocationsAreEqual(method,

    new MethodInvocation(new Object[]

            { font, color, string, position }));

    }
}

The MethodInvocation class stores the method name and parameters for a particular invocation. The static VerifyMethodInvocationsAreEqual method takes two MethodInvocation objects and does a deep comparison of them.

Getting access to the private Graphics object is where partial classes come in. My main ImageWriter class is tagged partial so I can extend it in my unit test source file and wrap access to private members:

internal sealed partial class ImageWriter

{

    internal Graphics Test_m_graphics

    {

    get { return m_graphics; }

        set { m_graphics = value; }

    }

}

Updating the test method to use this wrapper and the mock object is now easy:

[TestMethod]

public void Test_DrawString()

{

    Renderer renderer = new Renderer();

    using(Graphics graphics = new Graphics())

    {

        renderer.Test_m_graphics = graphics;

    Queue<MethodInvocation> methodList =

            new Queue<MethodInvocation>();

    methodList.Enqueue(new MethodInvocation("DrawString",

            new Object[]

    { new Font("Arial", 10f), Color.Black, "Test String",

    new RectangleF(0f, 0f, 10f, 10f) }));

    renderer.Graphics.MethodInvocations = methodList;

    renderer.ImageWriter.DrawString(new Font("Arial", 10f), Color.Black,

            "Test String",

    new RectangleF(0f, 0f, 10f, 10f));

    }

}

Test methods like this are easy to create and easy to understand. The biggest downside I see is that the assert phase of the AAA pattern is not immediately obvious, especially to someone not used to the C# using () statement. With that caveat, however, I like the way this flows and it just feels natural to me. And I think that’s the key to unit testing. It has to be easy to do, easy to maintain and fit naturally into your development style. If not, it will languish and become useless. Stale tests that either don’t compile or, worse yet, give false positives, are worse than no tests at all.