TDD Helps You Write Software That Makes Sense

This week, I was working on a project with a co-worker that has very few automated tests. There’s a SpecFlow project that does some integration testing, but it’s really clunky for fine-tuning and bug fixing. The problem with this project is that it’s a classic example of software built without a proper design and without any consideration for how it would be tested. The result is that you’ve got a huge mess of code with no intuitiveness.

Think About How You’d Expect the Software to Work

One of the things I’m very good at is writing software that works the way I expect it to. If I have some settings that are stored in an XML file, I think to myself, “iTunes wouldn’t require me to edit an XML file to change these settings. They’d give me a way to configure it through the UI.” And so, I build a way to configure my settings through the UI because that’s what I would expect a good application to do.

I apply the same logic to objects that I create in a class libraries. If there’s a piece of functionality that the class needs to provide, I provide a simple way to access it. I don’t expose a bunch of methods that need to be called in a particular order when it’s not necessary.

I wanted to simplify the messy project described earlier to make testing easier and more efficient. As I was thinking about how to accomplish this, I found myself asking the question, “How would I expect this to work?” And then, I would answer myself, “Well, I want to pass in <foo> and get back <bar>.” With that thought, a light came on: that’s a test–a test that should have been written before anything else was coded.

Focus On What You’re Trying to Accomplish

If the original developer would have taken a minute to think about how the software should work and write tests accordingly, this project would be in much better shape. In its current state, it’s mostly functional, but there is no way to hammer out the last few bugs other than by performing manual, end-to-end testing. As items are fixed, there is nothing in place to ensure nothing was broken. There is also no way to ensure that fixing future issues will not re-break the item that was just fixed.

A lot of developers struggle with writing tests first, and I think it’s because we learn to write tests by writing code first. “I can’t write the test because the methods don’t exist.” You’re thinking too implementation-y about the test. You’re writing a method. It should do one thing. What is that thing, and how can you test it? That’s the test you write. “But the method does way more than that!” Well, it’s probably a bad method. Whoever wrote it should have taken a minute upfront to think about what they were trying to accomplish with that method before creating a huge, unmanageable mess!

Make It Intuitive

I like “clean” and “simple” as characteristics of good software, but above all-else it needs to be intuitive. It’s not intuitive to browse the hard disk to find an XML configuration file to change configuration settings. It’s (generally) not intuitive to instantiate a class and call five different methods in order to accomplish a single task. The simplest thing you can do when creating a new class is to list out what you need from the class. Don’t worry about all the methods you’ll need to implement that functionality, just focus on the functionality.

Let’s say you have a requirement that the class needs to do X. So, how about creating a method called DoX? Now, you’re ready to write a test: DoX_DoesX. It’s easy to get straight to the meat for testing purposes, and you’ve exposed a clean, intuitive interface. What does DoX do? It does X.

As you build out functionality, continue to take time to think about what the object you’re creating should do. Maybe DoX needs some settings from the registry, but reading settings from the registry is not part of X. Don’t just shrug and put a bunch of registry stuff in your method! That adds unnecessary overhead for testing and, at the end of the day, it has nothing to do with the functionality that you’re interested in achieving with that method. Instead, create an ISettingsProvider interface and implement a RegistrySettingsProvider class to be used by your object, or simply provide the object with settings retrieved elsewhere in the application in its constructor.

Remember, hard to test usually means hard to use. Writing tests first help ensure that your code can be consumed easily and intuitively. If you find that your code is difficult to test, there’s probably a way to simplify to make it easier and improve the design.

Performance Profiling for Unit Tests

When I first got my hands on Visual Studio 2012, I was trying out every feature I could, new and old alike. I love the new Test Explorer. Code coverage is SO much better than it was before. Code analysis for code clones is a terrific, too. The only thing I wasn’t happy about was performance analysis.

The reason I wasn’t happy with performance analysis is that I couldn’t use it on unit tests. Luckily, Microsoft fixed that with the release of Visual Studio 2012 Update 1. Now you can right-click a test and choose Profile Test to run performance analysis on a single unit test, and it is awesome!

ProfileTest_ContextMenu

When you choose to profile a test, the test runs as usual followed by the analysis. When analysis completes, you’re presented with a summary view that shows you CPU usage over time, the “hot path” call tree—a tree of the most active function calls where most of the work was performed—and a list of functions responsible for doing the most individual work.

ProfileTest_SummaryView

You can find out more and investigate resource spikes by selecting a time range on the graph and filtering. That’s all good and well, but what really blew me away was that you can click on the functions with the most individual work to drill into them. Drill into them? Yea—you’re taken to a view that shows you the selected function, its callers, and the methods it calls. There are percentages that show how much time was taken in each of the three areas (callers, current method, and callees), and you can click into any of the methods displayed to navigate up or down the call stack. The actual code for the current method is also displayed. The only thing that seemed sub-optimal is that I couldn’t edit the code directly; there’s a link to the actual file, though, so you’re only a click away from the editable code file.

ProfileTest_FunctionDetails

There are other, sortable views you can look at, too. You can view a call tree or breakdown by module, and you can get to the same function details view described above from each of those views. It’s a really useful, powerful experience.

Here’s where it gets really nuts, though: add SpecFlow to the mix. SpecFlow lets you write feature-based scenarios that are capable of serving as automated integration tests. The scenarios run like normal unit tests. You can right-click them in the Test Explorer to run performance analysis on them. This means that you can do targeted performance analysis on specific features of your application! To test this out, I sorted my unit tests by execution duration and analyzed the slowest. I was able to find a bottleneck with a caching algorithm used by nearly all of the other modules in the application. Execution time of the 350 unit tests in the project went from 50 seconds to 20. That’s a HUGE improvement from fixing one flaw found from running analysis on one function picked only because it was the most time-consuming in the slowest test.

Good tests are supposed to run quickly, and performance analysis is an invaluable tool to help you triage valuable-but-poor-performing tests. Also, since you’ve got automated tests, you can refactor and optimize the performance of your application with high confidence. If you haven’t used performance analysis before—more specifically, performance analysis for unit tests—give it a shot; I’d be blown away if you didn’t find incredible value.

Test-first Development Cartoons

Lately, I’ve gotten into the habit of keeping a ton of can’t-forget-to-read-this articles open in separate tabs in my browser. It’s gotten to the point where I have so many tabs open that I can’t remember why or how I got to the tab in the first place. It’s kinda fun, though. Going back through the tabs feels like browsing new content that’s all be hand-picked by me for me. Thanks, me of the past!

I was flipping through some of the older tabs and found this Microsoft Patterns & Practices article about unit testing. This article is a bit long, but it serves as a good introduction to unit testing, touching on a variety of topics related to unit tests and automated system testing. There were a couple of cartoons about test-first development that I really liked in this article.

Writing unit tests before you write the code—test-first development—is recommended by most developers who have seriously tried it. Writing the tests for a method or interface makes you think through exactly what you want it to do. It also helps you discuss with your colleagues what is required of this particular unit. Think of it as discussing samples of how your code will be used.

For example, Mort, a developer at Fabrikam, has taken on the task of writing a method deep in the implementation of an ice-cream vending website. This method is a utility, likely to be called from several other parts of the application. Julia and Lars will be writing some of those other components. They don’t care very much how Mort’s method works, so long as it produces the right results in a reasonable time.

In the comic, Mort has been given some requirements and is left to come up with implementation details on his own. He writes tests to show an example of how his method will be used as well as to build in some assumptions. The point of test-first development is that you come up with a way to test before you begin coding. In practice, this is easier said than done, but it’s a good habit to get into. Writing tests first ensures that you write good, testable code. When you write the solution first, it’s much easier to end up with poorly tested or untested (gasp!) code.

The second comic introduces a new piece of functionality. Note that no code had been written at this point, but a new test is added to verify a different behavior of the same code. At the end of the comic, Mort points out that all that’s left to do now that the tests are in place is to write some code that makes the tests pass. Time is spent writing tests and thinking through requirements and scenarios up-front. Then, when you move on to the “real” development, it’s much easier because you’re just writing code to make the tests pass. (Way better than running code and repeating manual tests over and over again!)

Unit Testing and Private Methods

So you’re a TDD developer.  You code along merrily, writing failing unit tests, implementing them to a point of success, and then you refactor. But then, with no notice–WHAM–a private method. Now what!?

There are different schools of thought for this scenario. One camp says that if it’s not publicly exposed, you shouldn’t be testing it. I think that’s valid, but it’s equally valid to write test code for private methods. Just because a method’s private doesn’t mean it doesn’t deserve the same special attention. And just because I want to test a method that happens to be private doesn’t mean I want to expose it to the rest of the world! Let’s not worry about the debate for now. I’m going to show you how you can override private methods with Microsoft Fakes and how call private methods with Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.

Override private methods

Problem: You have a private method with behavior that’s disruptive to unit testing. Regardless of the cause of the disruption, you want to provide an alternate implementation.

Solution: Use Microsoft Fakes to shim the private method. (Need to know more about Fakes? Check out my tutorial!)

Here’s how you can do it:

  1. Create an instance of your class
  2. Create a shim from  your class
  3. Override the private method on the shim
  4. Invoke the test method

Here’s a code example:

[TestMethod]
public void SecretMethod_FunctionalityIsOverridden()
{
    using (ShimsContext.Create())
    {
        var target = new FileReader("foo");
        var shim = new ShimFileReader(target);

        bool calledShimMethod = false;
        shim.SecretMethod = () => 
        {
            calledShimMethod = true;
            return "bar";
        };

        target.Read();

        Assert.IsTrue(calledShimMethod);
    }
}

Call private methods

Problem: You want to test a private method, but you can’t because it’s private.

Solution: Use Microsoft Test Framework’s included PrivateObject class.

The only thing I don’t like about PrivateObject is that it (presumably) uses reflection for method names, which means method names will be coded as strings with no compile-time checks. It’s very easy to use, though, and if you need to invoke a private method, I haven’t seen a better way to do it!

Here are the steps:

  1. Create an instance of your class
  2. Create a PrivateObject from your class
  3. Use the PrivateObject‘s Invoke method

And here’s the code:

[TestMethod]
public void SecretMethod_ReturnsHumorousMessage()
{
    var target = new FileReader("foo");
    var privateObject = new PrivateObject(target);
    var actual = privateObject.Invoke("SecretMethod");

    Assert.IsNotNull(actual);
}

Putting it all together (useless, but fun!)

I was able to verify both of these capabilities by writing a single unit test that overrides the private method, then calls the private method. Check it out:

[TestMethod]
public void SecretMethod_FunctionalityIsOverridden()
{
    using (ShimsContext.Create())
    {
        var target = new FileReader("foo");
        var shim = new ShimFileReader(target);

        // override the private method
        bool calledShimMethod = false;
        shim.SecretMethod = () => 
        {
            calledShimMethod = true;
            return "bar";
        };

        // call the private method
        var privateObject = new PrivateObject(target);
        var actual = privateObject.Invoke("SecretMethod");

        // assert that calling the private method
        // used the overridden implementation
        Assert.IsTrue(calledShimMethod);
    }
}