Testing Hard-to-Test Code with Rhino Mocks

One of the most common gripes that I hear about getting started with unit testing is that the code isn’t testable or it doesn’t have a testable design. People will make claims like, “We need X hours to make the project testable before we can start writing tests.” Well, I don’t buy into that. There are techniques that you can employ to extract the pieces of code you’re modifying into little, testable nuggets that have nothing to do with the rest of the not-so-test-friendly project. I’m going to cover two of my favorite, most-commonly used techniques in this post.

Extract the code to be tested.

This is a pretty straightforward solution to a not-so-obvious problem. The most common scenario that I’ve seen for this technique is the “monster function.” I’m sure you’ve seen this before. It’s the 1000-line function that contains all the logic for the module or application you’re modifying. This is a classic hard-to-test situation because, at first glance, it seems like you need to set up everything necessary to complete the entire process in order to do any sort of unit testing.

To transform this mess into an unit testing masterpiece, you just need to do what nobody before you thought of: create a new function. Your new function can do whatever you need it to do. It can accept inputs. It can return outputs. And, it can do it all in a testable manner! So, make your function, test the hell out of it, then call your function from the appropriate place in the main function. Congratulations–you just made a unit tested change to a previously non-testable function.

Extract the code to be avoided.

The second scenario is a little more difficult, and the solution is a little less obvious. I use this technique when I’m dealing with external dependencies that aren’t enough to warrant an entire interface or when I’m working with tricky internal dependencies. Let’s look at an example. Consider the following function:

public class HardToTest
{
    public void ProcessFile()
    {
        string contents;
        using (var fs = new FileStream(@"c:\data.xml", FileMode.Open))
        {
            var sr = new StreamReader(fs);
            contents = sr.ReadToEnd();
        }
        // Do something interesting with file contents
    }
}

When sitting down to write a unit test for this function, you might be tempted to create a “data.xml” file in the root directory of the C drive during TestInitialize. That would probably result in a passing unit test on your machine, but please, don’t do that. You can trust that the FileStream and StreamReader objects will do their jobs correctly, so let’s just take them out of the equation for our unit testing. With a quick refactor, we end up with something like this:

public class HardToTest
{
    public void ProcessFile()
    {
        string contents = GetFileContents();
        // Do something interesting with file contents
    }
    
    internal protected virtual string GetFileContents()
    {
        using (var fs = new FileStream(@"c:\data.xml", FileMode.Open))
        {
            var sr = new StreamReader(fs);
            return sr.ReadToEnd();
        }
    }
}

Ok, great! Now, here’s where it gets fun. We can write a test for this function by mocking just the function we’re trying to avoid. Check it out:

[TestMethod()]
public void ProcessFileTest()
{
    HardToTest target = MockRepository.GeneratePartialMock();
    target.Expect(x => x.GetFileContents()).Return("File contents!");
    target.ProcessFile();
    target.VerifyAllExpectations();
}

Because we made our GetFileContents function virtual, the generated mock class can override its functionality. The Partial Mocks gives us this functionality; if the function were not overridden, it would be called as-is and would try to read from the hard-coded file path.

You could stick with this same approach to handle different scenarios that might arise in the isolated code. For instance, what would happen if the file did not exist? You would expect the isolated code to throw a System.IO.FileNotFoundException, right? Well, let’s test that!

[TestMethod]
[ExpectedException(typeof(System.IO.FileNotFoundException))]
public void ProcessFile_ThrowsFileNotFoundException()
{
    HardToTest target = MockRepository.GeneratePartialMock();
    target.Expect(x => x.GetFileContents()).Throw(
        new System.IO.FileNotFoundException());
    target.ProcessFile();
}

Some additional notes about this solution:

  • The function must be internal (see next note) protected (prevents calling of the function while setting expectation) virtual (allows function to be overridden).
  • The target project must expose its internals to the test project. This can be done by adding the “[assembly: InternalsVisibleTo(“YourTestProjectName”)]” attribute to the target project’s assembly.info file. You can read more about this attribute here.
Advertisement

Author: Adam Prescott

I'm enthusiastic and passionate about creating intuitive, great-looking software. I strive to find the simplest solutions to complex problems, and I embrace agile principles and test-driven development.

2 thoughts on “Testing Hard-to-Test Code with Rhino Mocks”

Leave a comment

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 )

Facebook photo

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

Connecting to %s

%d bloggers like this: