DaedTech

Stories about Software

By

Intro to Unit Testing 8: Test Suite Management and Build Integration

It’s been over a month now since my last post in this series, and for that I sort of apologize. I think I’ve been channelling all of my instructive energy into my now-finished Pluralsight course, leaving the blog largely for opinions, screeds, and a random hiring announcement. So, let’s get back on track and wrap this thing up. I have this post and another one slated and then we can call it a day.

So far, I’ve talked quite a lot about how and when (and when not) to write unit tests. I’ve offered up some techniques for helping you isolate the classes that you want to test, including the use of test doubles. And finally, I offered some advice on how to get people to leave you alone and let you write tests. So now I’d like to turn and offer some advice beyond just writing the things. You need to live with them, manage them and leverage them over the course of time.

Managing the Suite

You’ve built them. So, now what? At some point, you’ll wonder exactly when you’re getting started. For the first few or even few dozen classes you test, you’ll alternate between some exasperation at spending extra time doing something new and satisfaction at, well, doing something new. But then, at some point, you’ll be sitting around and notice that your test suite has like 400 tests and think, “wow, that’s a lot of code… do I really want all this?”

That feeling will hit you even harder when you go to change something under a tight deadline and your real quick change makes a test go red. You’re pretty sure the test is broken because it was testing the old way of doing things, so you really just want to comment out the test and you wonder why it’s such a pain to change the code. Why do you have to waste so much time to change one line of code?

The answer to these questions lies in practice but also effective test suite management. If you let the unit test suite become a boat anchor, it will drag you down. Your frustration will be real and reasonable, rather than just a temporary product of you being in a hurry and unfamiliar with working in a code base under test. You need to take care to prevent this from happening, and I’m going to tell you how in this section.

Name Your Tests Clearly and Be Wordy

When you’re writing a unit test, you’re looking at code. But when you’re running your test suite, you aren’t most of the time, and when you’re trying to understand why a run or a build failed, you’re never looking at code. When the test suite is failing, you don’t want to waste time figuring out why. And having to open the IDE, navigate to the test, read the code and figure out the problem is a waste of time.

Don’t give your test methods names like “Test24” or “CustomerTest” or something. Instead, give them names like “Customer_IsValid_Returns_False_When_Customer_SocialSecurityNumber_Is_Empty”. That method name may seem ridiculous, especially if you’re used to giving methods short names, but trust me, you’ll be thankful for it. When your build is failing, which of these method names would you rather see an X next to? Would you rather be saying “looks like test 24 is failing,” or would you rather be saying, “oh, I wonder why someone made it so that an empty SSN is now considered valid?” If you say the first one, you’re lying.

This may seem unimportant in the scheme of things, but it’s the difference between associating frustration and confusion with your test suite and viewing it as a warning system for potentially undesirable changes. The test suite needs to be communicating clearly to you what’s wrong. Descriptive test names help do that and they help you identify whether it’s your code or the test itself that needs to be changed in the face of changing requirements.

Make Your Test Suite Fast

Ruthlessly delete and cull out slow tests. I can’t say it more plainly than that. A good test suite runs in seconds, max. If yours starts to take minutes, or God forbid, hours, then it’s rotting and becoming useless to you. Think of it this way — if it takes several minutes to run the test suite, how often are you going to do it? Every time you make a change, or just when you check in? If it takes hours, will you ever run it voluntarily?

If your test suite takes a long time to run, nobody will run it. Short feedback loops are of paramount importance to developers, and we optimize for efficiency. If the unit test suite is inefficient, we’ll find other ways to get feedback. As such, it is incredibly important to ensure that your test suite always runs quickly. Treat it as if the rest of your team were waiting for any legitimate excuse not to use the test suite, and don’t let inefficiency be that excuse.

Test Code is First Class Code

A common mistake that I see among those relatively new to testing is test code that’s something of a mess. The code will be brittle, heavily duplicated, weird, and hard to read. In short, your tests and test classes will contain code that you wouldn’t be caught dead putting into production.

Don’t do that. Treat your test code as if it were any other code. Eliminate duplication. Factor common functionality out into methods. Be descriptive with naming and with the flow of the method. Keep that code clean. I get that there’s a desire when it comes to testing to make as much of a mess as possible in the “bug bash” sense of throwing chaos at the situation and proving that your code can handle it, but the chaos needs to be controlled, and you can control it by keeping your test code clean and maintainable. If the tests are clean and easy to maintain, people won’t mind going in periodically to make an adjustment. If they’re unruly, people will get annoyed and comment them out or stop running them.

Have a Single Assertion per Test

This is a subtle one, but it also goes toward maintainability. If you start writing tests that have 20 asserts in them, you may feel good that you’re exercising a whole section of the code, but really you’re making things hard for yourself later. If all 20 tests pass (or at least the first 19), then all will be executed. But if the first one fails, none of the rest get executed. This means that in test methods with lots of asserts, it’s not always clear where they’re failing, which means it’s not always clear what’s going wrong.

In order for your test suite to be an asset, it has to be a clear indicator of what’s going wrong. Which would you find more useful in your car: a series of many different lights with helpful diagrams that lit up to indicate a problem, or one unlabeled red light that came on whenever anything at all was wrong? If you had that latter light and it could mean anything from your gas being low to you being out of wiper fluid to imminent destruction of your transmission, I bet you’d just start ignoring it after a while.

Don’t Share State Between Your Tests

There is no more surefire way to drive yourself insane at some future date than by storing some kind of application state among unit tests being executed. What I mean is if you have some test A that declares sets a global counter variable to 1, and then you have another test B that depends on the global counter being set to 1 in order for it to succeed, you are in for a world of hurt.

The problem is that there is no guarantee that the unit test runner will execute the tests in any particular order. What’s likely to happen is that your tests get executed in a particular order whenever you run them on your machine, so everything goes fine. But when the build machine runs them they fail. Weird. So you check them on your friend Bob’s machine, and they pass there. But on Alice’s machine, they fail. If you didn’t already know why this was happening because I just told you, can you imagine how much of your hair you’d pull out? You’d probably be checking the IDE version on those machines, compiler information, OS settings, and God only knows what else. It’d be a wild goose chase.

And imagine if it worked on everyone’s machine initially and then six months later started failing occasionally on the build machine. Machine isn’t the only failing dimension — there’s also time. So please, whatever you do, do not have your unit tests depend on the execution of a previous test. This practice, more than any other, is likely to lead to a rage-quitting of unit testing as a practice where you simply take all of them out of the build.

Encourage Others and Keep Them Invested

This sounds like a strange one to round out the section, but it’s important. If you’re the only one fighting the good fight with unit tests, it becomes daunting and exasperating. Everyone else’s reaction to failing tests is annoyance and they’re waiting for excuses just to stop altogether. You wind up feeling that you’re in an adversarial relationship with the team (I speak from experience here). But if you get others to buy in, you’re not shouldering the burden alone and you have help keeping the suite healthy and helpful.

Build Integration

When you first start out unit testing, the tests will be sort of disorganized and haphazard. You’ll write a few to get the hang of it and then maybe discard them. After a bit of that, you’ll start checking them into your solution (unless you’re an incorrigible weirdo or a liar). You do that, and the suite grows and, ideally, everyone is running it locally to keep things clean and be notified of potential breaking changes.

But you have to take it beyond that at some point if you want to realize the full value of the unit tests. They can’t just be a thing everyone remembers to do locally on pain of nagging emails or because someone will buy the team donuts or some other peer-pressure-oriented demerit system. Failing unit tests have to have real (read: automated) consequences. And the best way to do this is to make it so that failing unit tests mean a failing build.

If you’re in a shop that’s not as formal, this may be difficult at first. One handicap may be that you’re reading this and saying “what do you mean by ‘the build?'” If what you do is write code and take some kind of executable out of your project’s output directory on your machine and push it to a server or to your users, you’ve got some work to do before you think about integrating unit tests. You need a build. A build is an automated process by which your source code is turned into a production-ready, deployable package. And it’s automated in the sense that it doesn’t involve you hitting Ctrl-Shift-B or Ctrl-F6 or whatever you do manually in your IDE to build. The Build, with a capital B, is a process that checks your code out of source control, builds it, runs checks and whatever else is necessary, perhaps increments the versioning of the executables, etc., and then spits out the final product that will be pushed to a server or burned onto a DVD or whatever. If you want to read more about build tools, you can google around about TeamCity, CruiseControl, TFS, FinalBuilder, Jenkins etc. And you don’t have to use a product like that — you can create your own using shell scripts or code if you choose.

Because of all the different options when it comes to programming languages, unit test technologies and build tools, I’m not going to offer a tutorial on how to integrate unit tests into your build. To be comprehensive, I’d need to give dozens of such tutorials. But what I will say is that your integration is going to take the same basic format no matter what tools you’re using. The build is a series of steps that passes if everything goes smoothly and the deliverables are ultimately generated. If a step in the build fails, then the build itself fails. What you need to do is add a step that involves running the unit tests. With this in place, you’re creating a situation where any failing unit test means that the entire build fails.

Conceptually, this is pretty straightforward. Unit test runners can be run in command line fashion and they’ll generate a return value of some kind. So the build tool needs to examine the test runner’s output for an error code. If it finds one, it puts the brakes on the whole operation.

It may seem extreme at first to torpedo the whole build because of a failing unit test, but when you think about it, what else should possibly happen? Why would you want a process that allowed you to ship code knowing that it was defective in a way that it didn’t used to be? That’s amateur hour. And, what’s more is that if your team starts understanding that failed unit tests mean a failed build they’ll be sure to run the tests before check-in so that they don’t fail. It will become a natural part of your process, and the quality of your software will be dramatically improved for it.

By

Intro to Unit Testing 7: Overcoming Inertia and Objections

In this series so far, I’ve introduced the concept of unit testing, talked about how to avoid early failures and frustrations, and then dived more in depth into the subject. But now that you’re familiar with the topic and reasonably well versed in it (particularly if you’ve been practicing over the course of this series), I’d like to use that familiarity to discuss why unit testing makes sense in terms that you now better understand. And beyond that, I’d like to discuss how you can use this sense to overcome inertia and even objections that others may have.

The Case for Unit Tests

Alright, so this is the part where I offer you a laundry list, right? This is where I say that unit tests improve code quality, document your code, exercise your API, promote (or at least correlate with) good design, help you break problems into manageable chunks, expose problems earlier when finding them is cheaper, and probably some other things that I’m forgetting right now. And I believe that all of these things are true. But after a number of years of faithfully writing unit tests and practicing test-driven development (TDD), I think that I can offer those as appetizers or footnotes in the face of the real benefit: they allow you to refactor your code without fear or hesitation.

First to Contend With: Inertia

Inertia could probably be categorized as an objection, but I’m going to treat it separately since it manifests in a different way. Objections to doing something are essentially counterarguments to it. They may be lame counterarguments or excellent counterarguments, but either way, they take an active position. Inertia doesn’t. It’s either ambivalence or passive-aggressive resistance. To illustrate the difference, consider the following exchanges:

Alice: We should start trying to get our code under test.
Bob: Unit testing is stupid and a complete waste of time.

versus

Alice: We should start trying to get our code under test.
Bob: Yeah, that’d be nice. We should do that at some point.

The former is a strident (and obtuse) counterargument while the latter is an example of inactivity by inertia. In the second exchange, Bob either thinks unit testing is a good idea — but just not now — or he’s blowing sunshine at Alice while subtly saying, “not now,” so that she’ll leave him alone and stop stumping for change (i.e. the passive-aggressive approach).

In either case, the best way to overcome inertia is to counteract it with activity of your own. Inertia is the byproduct of the developer (and human in general) tendency to get stuck in a rut of doing the comfortable and familiar, so overcoming it within your group is usually just a matter of creating a new rut for them. This isn’t necessarily an easy thing to do. You’ll have to write the tests, make sure they stay up to date, demonstrate the benefits to anyone who will listen, and probably spend some time teaching others how to do it. But if you persevere and your only obstacle is inertia, sooner or later test writing will become the new normal and you’ll get there.

Red Herrings and Stupid Objections

RedHerring

Around a year ago, I blogged about a guy who made a silly claim that he wrote a lot of unit tests but didn’t check them in. The reasoning behind this, as detailed in the post, was completely fatuous. But that’s to be expected since the purpose of this claim wasn’t to explain a calculated approach but rather to cover a lack of knowledge — the knowledge of how to write unit tests.

This sort of posturing is the province of threatened Expert Beginners. It’s the kind of thing that happens when the guy in charge understands that unit testing is widely considered to be table stakes for software development professionalism but has no idea how it works. As such, he believes that he has to come up with a rationale for why he’s never bothered to learn how to do it, but he’s handicapped in inventing an explanation that makes sense by virtue of the fact that he has no idea what he’s talking about. This results in statements like the following:

  • Unit tests prevent you from adapting to new requirements.
  • Testing takes too much time.
  • It wouldn’t work with our style of writing code.
  • You’re not going to catch every bug, so why bother?
  • Writing all of your tests before writing any code is dumb.
  • Management/customers wouldn’t like it and wouldn’t want to pay for it.

I obviously can’t cover all such possible statements, but use the smell test. If it sounds incredible or stupid, it probably is, and you’re likely dealing with someone who is interested in preserving his alpha status more than creating good work product. To be frank, if you’re in an environment like that, you’re probably best off practicing your craft on the sly. You can write tests and keep quiet about it or even keep them in your own personal source control (I have done both at times in my career when in this situation) to prevent people from impeding your own career development. But the best longer term strategy is to keep your eyes and ears open for other projects to work on where you have more latitude to set policies. Do an open source project in your spare time, grab an opportunity to develop a one-off tool for your group, or maybe even consider looking for a job at an organization a little more up to speed with current software development practices. You can stay and fight the good fight, but I don’t recommend it in the long run. It’ll wear you down, and you’re unlikely to win many arguments with people that don’t let a lack of knowledge stop them from claiming expertise on a subject.

I Don’t Know How to Unit Test

With inertia and silliness set aside, let’s move on to legitimate objections to the practice. At first blush, you may be inclined to scoff at the objection, “I don’t understand it,” particularly in an industry often dominated by people unwilling to concede that their knowledge is lacking in the slightest bit in any way whatsoever. But don’t scoff — this is a perfectly reasonable objection. It’s hard and often unwise simply to start doing something with professional stakes when you don’t know what you’re doing.

If the people around you admit to not knowing how to do it, this earnest assessment often indicates at least some willingness to learn. This is great news and something you can work with. Start teaching yourself how to do it so that you can help others. Watch Pluralsight videos and show them to your coworkers as well. If your group is amenable to it, you can even set aside some time to practice as a group or to bring in consultants to get you off to a good start. This is an objection that can easily be turned into an asset.

It Doesn’t Work With the Way I Code

I originally specked out this post in the series because of a comment in the very first post, and this section is the one that addresses that comment. Arguments of this form are ones I’ve heard quite frequently over the years, and the particulars of the coding style in question vary, but the common thread is the idea that unit testing creates friction with an established routine. This isn’t the same as “I don’t know how to unit test” because the people who say this generally do know how to write tests — they must or else they wouldn’t know anything about the subject and would make up Expert-Beginner-style stupid excuses. It also isn’t the same as the inertia objection because they’re saying, “I was willing to try, but I find that this impedes me,” rather than, “meh, I dunno, I like my routine.”

My short answer to someone who has this objection is, to put it bluntly, “change the way you code.” Whatever the specifics of your approach, when you’re done, you don’t wind up with a fast-executing safety net of tests that you trust — tests that document your intentions, keep your code flexible, help prevent regressions, and force your design to be easy to use and decoupled. People who code differently than you do, in that they unit test, do wind up with those things. So figure out a way to be one of those people.

On a deeper level, though, I understand this objection because it hits closer to home. I was never the type to bluster like an Expert Beginner, nor am I prone in the slightest to inertia. (I am pretty much the opposite, frequently biting off more than I can chew.) The other objections never really applied to me, but this one did both prior to starting to write tests and prior to adopting TDD as my exclusive approach to developing. You can read that latter perspective from my very early days of blogging. Years ago, I chafed at the prospect of unit testing because spending the extra time took me out of the ‘flow’ of my coding, and I balked at TDD because I thought “why would I start writing unit tests for this code when it might be refactored completely later?” In other words, neither one of these worked with my approach.

But in both cases, I relented eventually and changed the way I coded. I didn’t just one day say, “well, I guess I’ll just start writing code differently from now on.” What happened instead was that I realized that a lot of really bright people and prominent names in the industry had coding and design styles that were compatible with writing tests so it was at least worth trying things their way. It wasn’t a matter of doing something because the cool kids were doing it or resolving to change my ways. Rather, I thought to myself, “I’ll see what all the fuss is about. Then, I’ll either like it or go back to my way of doing things armed with much better arguments as to why my way is better.” So I poured myself into a different way of doing things, forced myself to keep at it even though it was slow and awkward, and, wouldn’t you know it, I eventually came to prefer it.

Convincing people in your group to follow that lead is not going to be easy, but it is doable. The best way to do it is to earn their respect and show them results. If your code is cleaner and freer of bugs and your past projects are easy to adapt and modify, people are going to notice and ask you what your secret is, and you’ll be in a position to show them. It may seem improbable to you now, but you can go from being a person quietly teaching yourself to unit test on the side to one of the key leaders in a software department relatively quickly. You just have to commit yourself to continuous improvement and moving toward proven, effective techniques. Unit testing is just one such technique, but it is a powerful and important one.

By

Introduction to Unit Testing Part 6: Test Doubles

In the last two posts in this series, I talked about how to test new code in your code base and then how to bring your legacy code under test. Toward the end of the last chapter in this series, I talked a bit about the concept of test doubles. The example I showed was one in which I used polymorphism to create a “dummy” class that I used in a test to circumvent otherwise untestable code. Here, I’ll dive into a lot more detail on the subject, starting out with a much simpler example than that and building to a more sophisticated way to handle the management of your test doubles.

First, a Bit of Theory

Before we get into test doubles, however, let’s stop and talk about what we’re actually doing, including theory about unit tests. So far, I’ve showed a lot of examples of unit tests and talked about what they look like and how they work (for instance, here in post two where I talk about Arrange, Act Assert). But what I haven’t addressed, specifically, is how the test code should interact with the production code. So let’s talk about that a bit now.

By far the most common case when unit testing is that you instantiate a class under test in the “arrange” part of your unit test, and then you do whatever additional setup is necessary before calling some method on that class. Then you assert something that should have happened as a result of that method call. Let’s return to the example of prime finder from earlier and look at a simple test:

[TestMethod]
public void Returns_False_For_One()
{
    var primeFinder = new PrimeFinder(); //Arrange

    bool result = primeFinder.IsPrime(1); //Act

    Assert.IsFalse(result); //Assert
}

This should be reviewed from the perspective of “arrange, act, assert,” but let’s look specifically at the “act” line. Here is the real crux of the test; we’re writing tests about the IsPrime method and this is where the action happens. In this line of code, we give the method an input and record its output, so it’s the perfect microcosm for what I’m going to discuss about a class under test: its interactions with other objects. You see, unit testing isn’t about executing your code — you can do that with integration tests, console apps, or even just by running the application. Unit testing, at its core, is about isolating your classes and running experiments on them, as if you were a scientist in a lab. And this means controlling all of the inputs to your class — stimulus, if you will — so that you can observe what it puts out.

Controlling the inputs in the PrimeFinder class is simple. Because I’m telling you that there are no invocations of global/static state (which will become an important theme as we proceed). You can see by looking at the unit test that the only input to the class under test (CUT) is the integer 1. This means that the only input/stimulus that we supply to the class is a simple integer, making it quite easy to make assertions about its behavior. Generally speaking, the simpler the inputs to a class, the easier that class is to test.

There are Inputs and There are Inputs

Omitting certain edge cases I can think of (and probably some that I’m not thinking of), let’s consider a handful of relatively straightforward ways that a class might get ahold of input information. There is what I did above — passing it into a method. Another common way to give information to a class is to use constructor parameters or setter methods/properties. I’ll refer to these as “passive collaboration” from the perspective of the CUT, since it’s simply being given the things that it needs. There is also what I’ll call “semi-passive collaboration,” which is when you pass a dependency to the CUT and the CUT interacts in great detail with that dependency, mutating its state and querying it. An example of this would be “Car theCar = new Car(new Engine())”, in which performing operations on Car related to starting and driving result in rather elaborate modifications to the state of Engine. It’s still passive in the sense that you’re handing the Engine class to the car, but it’s not as passive as simply handing it an integer. In general, passive input is input that the scope instantiating the CUT controls — constructor parameters, method parameters, setters, and even things returned from methods of objects passed to the CUT (such as the Car class calling _engine.GetTemperature() in the example in this paragraph).

In contrast, there is also “active collaboration,” which is when the CUT takes responsibility for getting its own inpu. This is input that you cannot control when instantiating the class. An example of this is a call to some singleton or public static method in the CUT. The only way that you can reassume control is by not calling the method in which it occurs. If static/singleton calls occur in the constructor, you simply cannot test or even instantiate this class without it doing whatever the static code entails. If it retrieves values from static state, you have no control over those values (short of mocking up the application’s global state).

A second form of active collaboration is the “new” operator. This is very similar to static state in that when you create the CUT, you have no control over this kind of input to the CUT. Imagine if Car new-ed up its own Engine and queried it for temperature. There would be absolutely no way that you could have any effect on this operation in the Car class short of not instantiating it. Like static calls, object instantiation renders your CUTs a non-negotiable, “take it or leave it” proposition. You can have them with all of their instantiated objects and global state or you can write your own, buddy.

Not all inputs to a class are created equal. There are a CUT’s passive inputs, in which the CUT cedes control to you. And then there are the CUT’s active inputs that it controls and on which it does not allow you to interpose in any way. As it turns out, it is substantially easier to test CUTs with exclusively passive collaboration/input and difficult or even impossible to test CUTs with active collaboration. This is simply because you cannot isolate actively collaborating CUTs.

Literals: Too Simple to Need Test Doubles

There’s still a little bit of work to do before we discuss test doubles in earnest. First, we have to talk about inputs that are too simple to require stand-ins: literals. The PrimeFinder test above is the perfect example of this. It’s performing a mathematical operation using an integer input, so what we’re interested in testing is known input-output pairs in a functional sense. As such, we just need to know what to pass in, to pass that value in, and then to assert that we get the expected return value.

In a strict sense, we could refer to this as a form of test double. After all, we’re doing a non-production exercise with the API, so the value we’re passing in is fake, in a sense. But that’s a little formal for my taste. It’s easier just to think in terms of literals almost always being too simple to require any sort of substitution of behavior.

An interesting exception to this the null literal (of null type) or the default value of a non-nullable type. In many cases, you may actually want to be testing this as an input since null and 0 tend to be particularly interesting inputs and the source of corner cases. However, in some cases, you may be supplying what is considered the simplest form of test double: the dummy value. A dummy value is something you pass into a function to say, “I don’t care what this is and I’m just passing in something to make the compiler happy.” An example of where you might do this is passing null to a constructor of an object instance when you just want to make assertions as to what some of its property values initialize to.

Simple/Value Objects and Passing in Friendlies

Next up for consideration is the concept of a “test stub,” or what I’ll refer to in the general sense as a “friendly.”

Friendlies

Take a look at this code:

public class Car
{
    public int EngineTemperature { get; private set; }

    public Car(Engine engine)
    {
        EngineTemperature = engine.TemperatureInFahrenheit;
    }
}
public class Engine
{
    public int TemperatureInFahrenheit { get; set; }
}

Here is an incredibly simple implementation of the Car-Engine pair I described earlier. Car is passed an Engine and it queries that Engine for a local value that it exposes. Let’s say that I now want to test that behavior. I want to test that Car’s EngineTemperature property is equal to the Engine’s temperature in fahrenheit. What do you think is a good test to write? Something like this, maybe —

[TestMethod]
public void EngineTemperature_Initializes_Value_Returned_By_Engine()
{
    const int engineTemperatureFromEngine = 200;
    Engine engine = new Engine() { TemperatureInFahrenheit = engineTemperatureFromEngine };
    var car = new Car(engine);

    Assert.AreEqual(engineTemperatureFromEngine, car.EngineTemperature);
}

Here, we’re setting up the Engine instance in such a way as that we control what it provides to Car when Car uses it. We know by inspecting the code for Car that Car is going to ask Engine for its TemperatureInFahrenheit value, so we set that value to a known commodity, allowing us to compare in the Assert. To put it another way, we’re supplying input indirectly to Car by setting up Engine and telling Engine what to give to Car. It’s important to note that this is only possible because Car accepts Engine as an argument. If Car instantiated Engine in its constructor, it would not be possible to isolate Car because any test of Car’s initial value would necessarily also be a test of Engine, making the test an integration test rather than a unit test.

Creating Bonafide Mocks

That’s all well and good, but what if the Engine class were more complicated or just written differently? What if the way to get the temperature was to call a method and that method went and talked to a file or a database or something? Think of how badly the testing for this is going to go:

public class Car
{
    public int EngineTemperature { get; private set; }

    public Car(Engine engine)
    {
        EngineTemperature = engine.TemperatureInFahrenheit;
    }
}
public class Engine
{
    public int TemperatureInFahrenheit
    {
        get
        {
            var stream = new StreamReader(@"C:\whatever.txt");
            return int.Parse(stream.ReadLine());
        }
    }
}

Now, when we instantiate a Car and query its engine temperature property, suddenly file contents are being read into memory and, as I’ve already covered in this series, File I/O is a definite no-no in a unit test. So I suppose we’re hosed. As soon as Car tries to read Engine’s temperature, we’re going to explode — or we’re going to succeed, which is even worse because now you’ll have a unit test suite that depends on the machine it’s running on having the file C:\whatever.txt on it and containing an integer as its first line.

But what if we got creative the way we did at the end of the last episode of this series? Let’s make the TemperatureInFahrenheit property virtual and then declare the following class:

public class FakeEngine : Engine
{
    private int _temperature;

    public override int TemperatureInFahrenheit
    {
        get { return _temperature; }
    }

    public FakeEngine(int temperature)
    {
        _temperature = temperature;
    }

This class is test-friendly because it doesn’t contain any file I/O at all and it inherits from Engine, overriding the offending methods. Now we can write the following unit test:

[TestMethod]
public void EngineTemperature_Initializes_Value_Returned_By_Engine()
{
    const int engineTemperatureFromEngine = 200;
    Engine engine = new FakeEngine(engineTemperatureFromEngine);
    var car = new Car(engine);

    Assert.AreEqual(engineTemperatureFromEngine, car.EngineTemperature);
}

If this seems a little weird to you, remember that our goal here is to test the Car class and not the engine class. All that the Car class knows about Engine is that it wants its TemperatureInFahrenheit property. It doesn’t (and shouldn’t) care how or where this comes from internally to Engine — file I/O, constructor parameter, secret ink, whatever. And when testing the Car class, you certainly don’t care. Another way to think of this is that you’re saying, “assuming that Engine tells Car that the engine temperature is 200, we want to assert that Car’s EngineTemperature property is 200.” In this fashion, we have isolated the Car class and are testing only its functionality.

This kind of test double and testing technique is known as a Fake. We’re creating a fake engine to stand-in for the real one. It’s not simple enough to be a dummy or a stub, since it’s a real, bona-fide different class instead of a doctored version of an existing one. I realize that the terminology for the different kinds of test doubles can be a little confusing, so here’s a helpful taxonomy of them.

Mocking Frameworks

The last step in the world of test doubles is to get to actual mock objects. If you stop and ponder the fake approach from the last section a bit, a problem might occur to you. The problem has to do with long-term maintenance of code. I remember, many moons ago when I discovered the power of polymorphism for creating fake objects, that I thought it was the greatest thing under the sun. Obviously there was at least one fake per test class with dependency, and sometimes there were multiple dependencies. And I didn’t always stop there — I might define three or four different variants of the fake, each having a method that behaved differently for the test in question. In one fake, TemperatureInFarenheit would return a passed in value, but in another, it would throw an exception. Oh, there were so many fakes — I was swimming in fakes for classes and fakes for interfaces.

And they were awesome… until I added a method to the interface they implemented or changed behavior in the class they inherited. And then, oh, the pain. I would have to go and change dozens of classes. And then there was also the fact that all of this faking took up a whole lot of space. My test classes were littered with nested classes of fakes. It was fun at first, but the maintenance became a drudgery. But don’t worry, because my gift to you is to spare you that pain.

What if I told you that you could implement interfaces and inherit from classes anonymously, without actually creating source code that did this? I’d be oversimplifying a bit, but once you got past that, you’d probably be pretty excited. I say this because, as you start to grasp the concept of mocking frameworks, this kind of “dynamic interface implementation/inheritance” is the easiest way to reason about what it’s doing, from a practical perspective, without getting bogged down in more complicated concepts like reflection and direct work with byte-code and other bits of black magic.

As an example of this in action, take a look at how I go about testing the Car and Engine with the difficult dependency. The first thing that I do is delete the Fake class because there’s no need for it. The next thing I do is write a unit test, using a framework called JustMock by Telerik (this is currently my preferred mocking framework for C#).

[TestMethod]
public void EngineTemperature_Initializes_Value_Returned_By_Engine()
{
    const int engineTemperatureFromEngine = 200;
            
    var engine = Mock.Create();
    engine.Arrange(e => e.TemperatureInFahrenheit).Returns(engineTemperatureFromEngine);

    var car = new Car(engine);

    Assert.AreEqual(engineTemperatureFromEngine, car.EngineTemperature);
}

Notice that instead of instantiating an engine, I now invoke a static method on a class called Mock that takes care of creating my dynamic inheritor for me. Mock.Create() is what creates the equivalent of FakeEngine. On the next line, I invoke an (extension) method called Arrange that creates an implementation of the property for me as well. What I’m saying, in plain English, is “take this mock engine and arrange it such that the TemperatureInFahrenheit property returns 200.” I’ve done all of this in one line of code instead of adding an entire nested class. And, best of all, I don’t need to change this mock if I decide to change some behavior in the base class or add a new method.

Truly, once you get used to the concept of mocking, you’ll never go back. It will become your best friend for the purposes of mocking out dependencies of any real complexity. But temper your enthusiasm just a bit. It isn’t a good idea to use mocking frameworks for simple dependencies like the PrimeFinder example. The lite version of JustMock that I’ve used and many others won’t even allow it, and even if they did, that’s way too much ceremony — just pass in real objects and literals, if you can reasonably.

The idea of injecting dependencies into classes (what I’ve called “passive” and “semi-passive” collaboration) is critical to mocking and unit testing. All basic mocking frameworks operate on the premise that you’re using this style of collaboration and that your classes are candidates for polymorphism (either interfaces or overridable classes). You can’t mock things like primitives and you can’t mock sealed/final classes.

There are products out there called isolation frameworks that will grant you the ability to mock pretty much everything — primitives, sealed/final classes, statics/singletons, and even the new operator. These are powerful (and often long-running, resource-intensive) tools that have their place, but that place is, in my opinion, at the edges of your code base. You can use this to mock File.Open() or new SqlConnection() or some GUI component to get the code at the edge of your application under test.

But using it to test your own application logic is a path that’s fraught with danger. It’s sort of like fixing a broken leg with morphine. Passively collaborating CUTs have seams in them that allow easy configuration of behavior changes and a clear delineation of responsibilities. Actively collaborating CUTs lack these things and are thus much more brittle and difficult to separate and modify. The fact that you can come up with a scheme allowing you to test the latter doesn’t eliminate these problems — it just potentially masks them. I will say that isolating your coupled, actively collaborating code and testing it is better than not testing it, but neither one is nearly as good as factoring toward passive collaboration.

By

Intro to Unit Testing 5: Invading Legacy Code in the Name of Testability

If, in the movie Braveheart, the Scots had been battling a nasty legacy code base instead of the English under Edward Longshanks, the conversation after the battle at Stirling between Wallace and minor Scottish noble MacClannough might have gone like this:

Wallace: We have prevented new bugs in the code base by adding new unit tests for all new code, but bugs will still happen.

MacClannough: What will you do?

Wallace: I will invade the legacy code, and defeat the bugs on their own ground.

MacClannough (snorts in disbelief): Invade? That’s impossible.

Wallace: Why? Why is that impossible? You’re so concerned with squabbling over the best process for handling endless defects that you’ve missed your God-given right to something better.

Braveheart

Goofy as the introduction to this chapter of the series may be, there’s a point here: while unit testing brand new classes that you add to the code base is a victory and brings benefit, to reap the real game-changing rewards you have to be a bit of a rabble-rouser. You can’t just leave that festering mass of legacy code as it is, or it will generate defects even without you touching it. Others may scoff or even outright oppose your efforts, but you’ve got to get that legacy code under test at some point or it will dominate your project and give you unending headaches.

So far in this series, I’ve covered the basics of unit testing, when to do it, and when it might be too daunting. Most recently, I talked about how to design new code to make it testable. This time, I’m going to talk about how to wrangle your existing mess to start making it testable.

Easy Does It

A quick word of caution here before going any further: don’t try to do too much all at once. Your first task after reading the rest of this post should be selecting something small in your code base to try it on if you want to target production and get it approved by an architect or lead, if that’s required. Another option is just to create a playpen version of your codebase to throw away and thus earn yourself a bit more latitude, but either way, I’d advise small, manageable stabs before really bearing down. What specifically you try to do is up to you, but I think it’s worth proceeding slowly and steadily. I’m all about incremental improvement in things that I do.

Also, at the end of this post I’ll offer some further reading that I highly recommend. And, in fact, I recommend reading it before or as you get started working your legacy code toward testability. These books will be a great help and will delve much further into the subjects that I’ll cover here.

Test What You Can

Perhaps this goes without saying, but let’s just say it anyway to be thorough. There will be stuff in the legacy code base you can test. You’ll find the odd class with few dependencies or a method dangling off somewhere that, for a refreshing change, doesn’t reference some giant singleton. So your first task there is writing tests for that code.

But there’s a way to do this and a way not to do this. The way to do it is to write what’s known as characterization tests that simply document the behavior of the existing system. The way not to do this is to introduce ‘corrections’ and cleanup as you go. The linked post goes into more detail, but suffice it to say that modifying untested legacy code is like playing Jenga — you never really know ahead of time which brick removal is going to cause an avalanche of problems. That’s why legacy code is so hard to change and so unpleasant to work with. Adding tests is like adding little warnings that say, “dude, not that brick!!!” So while the tower may be faulty and leaning and of shoddy construction, it is standing and you don’t want to go changing things without putting your warning system in place.

So, long story short, don’t modify — just write tests. Even if a method tells you that it adds two integers and what it really does is divide one by the other, just write a passing test for it. Do not ‘fix’ it (that’ll come later when your tests help you understand the system and renaming the method is a more attractive option). Iterate through your code base and do it everywhere you can. If you can instantiate the class to get to the method you want to test and then write asserts about it (bearing in mind the testability problems I’ve covered like GUI, static state, threading, etc), do it. Move on to the next step once you’ve done the easy stuff everywhere. After all, this is easy practice and practice helps.

Go searching for extractable code

Now that you have a pretty good handle on writing testable code as you’re adding it to the code base and getting untested but testable code under test, it’s time to start chipping away at the rest. One of the easiest ways to do this is to hunt down methods in your code base that you can’t test but not because of the contents in them. Here are two examples that come to mind:

public class Untestable1
{
    public Untestable1()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
    }

    public int AddTwoNumbers(int x, int y)
    {
        return x + y;
    }
}

public class Untestable2
{
    public void PerformSomeBusinessLogic(CustomerOrder order)
    {
        Console.WriteLine("Total is " + AddTwoNumbers(order.Subtotal, order.Tax));
    }

    private int AddTwoNumbers(int x, int y)
    {
        return x + y;
    }
}

The first class is untestable because you can’t instantiate it without kicking off global state modification and who knows what else. But the AddTwoNumbers method is imminently testable if you could remove that roadblock. In the second example, the AddTwoNumbers method is testable once again, in theory, but with a roadblock: it’s not public.

In both cases, we have a simple solution: move the method somewhere else. Let’s put it into a class called “BasicArithmeticPerformer” as shown below. I do realize that there are other solutions to make these methods testable, but we’ll talk about them later. And I’ll tell you what I consider to be a terrible solution to one of the testability issues that I’ll talk about now: making the private method public or rigging up your test runner with gimmicks to allow testing of private methods. You’re creating an observer effect with testing when you do this — altering the way the code would look so that you can test it. Don’t compromise your encapsulation design to make things testable. If you find yourself wanting to test what’s going on in private methods, that’s a strong, strong indicator that you’re trying to test the wrong thing or that you have a design flaw.

public class BasicArithmeticPerformer
{
    public int AddTwoNumbers(int x, int y)
    {
        return x + y;
    }
}

Now that’s a testable class. So what do the other classes now look like?

public class Untestable1
{
    public Untestable1()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
    }

    private int AddTwoNumbers(int x, int y)
    {
        return new BasicArithmeticPerformer().AddTwoNumbers(x, y);
    }
}

public class Untestable2
{
    public void PerformSomeBusinessLogic(CustomerOrder order)
    {
        Console.WriteLine("Total is " + AddTwoNumbers(order.Subtotal, order.Tax));
    }

    public int AddTwoNumbers(int x, int y)
    {
        return new BasicArithmeticPerformer().AddTwoNumbers(x, y);
    }
}

Yep, it’s that simple. In fact, it has to be that simple. Modifying this untestable legacy code is like walking a high-wire without a safety net, so you have to change as little as possible. Extracting a method to another class is very low risk as far as refactorings go since the most likely problem that could possibly occur (particularly if using an automated tool) is non-compiling. There’s always a risk, but getting legacy code under test is lower risk in the long run than allowing it to continue rotting and the risk of this particular approach is minimal.

On the other side of things, is this a significant win? I would say so. Even ignoring the eliminated duplication, you now have gone from 0 test coverage to 50% in these classes. Test coverage is not a goal in and of itself, but you can now rest a little easier knowing that you have a change warning system in place for half of your code. If someone comes along later and says, “oh, I’ll just change that plus to a minus so that I can ‘reuse’ this method for my purposes,” you’ll have something in place that will throw up a bid red X and say, “hey, you’re breaking things!” And besides, Rome wasn’t built in a day — you’re going to be going through your code base building up a test suite one action like this at a time.

Code that refers to no class fields is easy when it comes to extracting functionality to a safe, testable location. But what if there is instance-level state in the mix? For example…

public class Untestable3
{
    int _someField;

    public Untestable3()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
        _someField = TestabilityKiller.Instance.GetSomeGlobalVariableValue();
    }

    public int AddToGlobal(int x)
    {
        return x + _someField;
    }
}

That’s a little tougher because we can’t just pull _someField into a new, testable class. But what if we made a quick change that got us onto more familiar ground? Such as…

public class Untestable3
{
    int _someField;

    public Untestable3()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
        _someField = TestabilityKiller.Instance.GetSomeGlobalVariableValue();
    }

    public int AddToGlobal(int x)
    {
        return AddTwoNumbers(x, _someField);
    }

    private int AddTwoNumbers(int x, int y)
    {
        return x + y;
    }
}

Aha! This looks familiar, and I think we know how to get a testable method out of this thing now. In general, when you have class fields or local variables, those are going to become arguments to methods and/or constructors of the new, testable class that you’re creating and instantiating. Understand going in that the more local variables and class fields you have to deal with, the more of a testing headache the thing you’re extracting is going to be. As you go, you’ll learn to look for code in legacy classes that refers to comparably few local variables and especially fields in the current class as a refactoring target, but this is an acquired knack.

The reason this is not especially trivial is that we’re nibbling here at an idea in static analysis of object oriented programs called “cohesion.” Cohesion, explained informally, is the idea that units of code that you find together belong together. For example, a Car class with an instance field called Engine and three methods, StartEngine(), StopEngine( )and RestartEngine() is highly cohesive. All of its methods operate on its field. A class called Car that has an Engine field and a Dishwasher field and two methods, StartEngine() and EmptyDiswasher() is not cohesive. When you go sniping for testable code that you can move to other classes, what you’re really looking for is low cohesion additions to existing classes. Perhaps some class has a method that refers to no instance variables, meaning you could really put it anywhere. Or, perhaps you find a class with three methods that refer to a single instance variable that none of the other 40 methods in a class refer to because they all use some other fields on the class. Those three methods and the field they use could definitely go in another class that you could make testable.

When refactoring toward testability, non-cohesive code is the low-hanging fruit that you’re looking for. If it seems strange that poorly designed code (and non-cohesive code is a characteristic of poor design) offers ripe refactoring opportunities, we’re just making lemonade out of lemons. The fact that someone slammed unrelated pieces of code together to create a franken-class just means that you’re going to have that much easier of a time pulling them apart where they belong.

Realize that Giant Methods are Begging to be Classes

It’s getting less and less common these days, but do you ever see object-oriented code which you can tell that the author meandered his way over to from writing C back in the one-pass compiler days? If you don’t know what I mean, it’s code that has this sort of form:

public void PerformSomeBusinessLogic(CustomerOrder order)
{
    int x, y, z;
    double a, b, c;
    int counter;
    CustomerOrder tempOrder;
    int secondLoopCounter;
    string output;
    string firstTimeInput;
            
    //Alright, now let's get started because this is going to be looooonnnng method...
    ...
}

C programmers wrote code like this because in old standards of C it was necessary to declare variables right after the opening brace of a scope before you started doing things like assignment and control flow statements. They’ve carried it forward over the years because, well, old habits die hard. Interestingly, they’re actually doing you a favor. Here’s why.

When looking at a method like this, you know you’re in for doozy. If it has this many local variables, it’s going to be long, convoluted and painful. In the C# world, it probably has regions in it that divide up the different responsibilities of the method. This is also a problem, but a lemons-to-lemonade opportunity for us. The reason is that these C-style programmers are actually telling you how to turn their giant, unwieldy method into a class. All of those variables at the top? Those are your class fields. All of those regions (or comments in languages that don’t support regioning)? Method names.

In one of the resources I’ll recommend, “Uncle” Bob Martin said something along the lines of “large methods are where classes go to hide.” What this means is that when you encounter some gigantic method that spans dozens or hundreds of lines, what you really have is something that should be a class. It’s functionality that has grown too big for a method. So what do you do? Well, you create a new class with its local variables as fields, its region names/comments as method titles, and class fields as dependencies, and you delegate the responsibility.

public class Untestable4
{
    public void PerformSomeBusinessLogic(CustomerOrder order)
    {
        var extractedClass = new MaybeTestable();
        extractedClass.Region1Title();
        extractedClass.Region2Title();
        extractedClass.Region3Title();
    }
}

public class MaybeTestable
{
    int x, y, z;
    double a, b, c;
    int counter;
    CustomerOrder tempOrder;
    int secondLoopCounter;
    string output;
    string firstTimeInput;

    public void Region1Title()
    {

...

In this example, there are no fields in the untestable class that the method is using, but if there were, one way to handle this is to pass them into the constructor of the extracted class and have them as fields there as well. So, assuming this extraction goes smoothly (and it might not be that easy if the giant method has a lot of temporal coupling, resulting from, say, recycled variables), what is gained here? Well, first of all, you’ve slain a giant method, which will inevitably be good from a design perspective. But what about testability?

In this case, it’s possible that you still won’t have testable methods, but it’s likely that you will. The original gigantic method wasn’t testable. They never are. There’s really way too much going on in them for meaningful testing to occur — too many control flow statements, loops, global variables, file I/O, etc. Giant methods are giant because they do a lot of things, and if you do enough code things you’re going to start running over the bounds of testability. But the new methods are going to be split up and more focused and there’s a good chance that at least one of them will be testable in a meaningful way. Plus, with the extracted class, you have control over the new constructor that you’re creating whereas you didn’t with the legacy class, so you can ensure that the class can at least be instantiated. At the end of the day, you’re improving the design and introducing a seam that you can get at for testing.

Ask for your dependencies — don’t declare them

Another change you can make that may be relatively straightforward is to move dependencies out of the scope of your class — especially icky dependencies. Take a look at the original version of Untestable3 again.

public class Untestable3
{
    int _someField;

    public Untestable3()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
        _someField = TestabilityKiller.Instance.GetSomeGlobalVariableValue();
    }

    public int AddToGlobal(int x)
    {
        return x + _someField;
    }
}

When instantiated, this class goes and rattles some global state cages, doing God-knows-what (icky), and then retrieves something from global state (icky). We want to get a test around the AddToGlobal method, but we can’t instantiate this class. For all we know, to get the value of “someField” the singleton gets the British Prime Minster on the phone and asks him for a random number between 1 and 1000 — and we can’t automate that in a test suite. Now, the earlier option of extracting code is, of course, viable, but we also have the option of punting the offending code out of this class. (This may or may not be practical depending on where and how this class is used, but let’s assume it is). Say there’s only one client of this code:

public class Untestable3Client
{
    public void SomeMethod()
    {
        var untestable = new Untestable3();
        untestable.AddToGlobal(12);
    }
}

All we really want out of the constructor is a value for “_someField”. All of that stuff with the singleton is just noise. Because of the nature of global variables, we can do the stuff Untestable3’s constructor was doing anywhere. So what about this as an alternative?

public class Untestable3Client
{
    public void SomeMethod()
    {
        TestabilityKiller.Instance.DoSomethingHorribleWithGlobalVariables();
        var someField = TestabilityKiller.Instance.GetSomeGlobalVariableValue();
        var untestable = new Untestable3(someField);
        untestable.AddToGlobal(12);
    }
}

public class Untestable3
{
    int _someField;

    public Untestable3(int someField)
    {
        _someField = someField;
    }

    public int AddToGlobal(int x)
    {
        return x + _someField;
    }
}

This new code is going to do the same thing as the old code, but with one important difference: Untestable3 is now a liar. It’s a liar because it’s testable. There’s nothing about global state in there at all. It just takes an integer and stores it, which is no problem to test. You’re an old pro by now at unit testing that’s this easy.

When it comes to testability, the new operator and global state are your enemies. If you have code that makes use of these things, you need to punt. Punt those things out of your code by doing what we did here: executing voids before your constructors/methods are called and asking for things returned from global state or new in your constructors/methods. This is another pretty low-impact way of altering a given class to make it testable, particularly when the only problem is that a class is instantiating untestable classes or reaching out into the global state.

Ruthlessly Eliminate Law of Demeter Violations

If you’re not familiar with the idea, the Law of Demeter, or Principle of Least Knowledge, basically demands that methods refer to as few object instances as possible in order to do their work. You can look at the link for more specifics on what exactly this “law” says, and what exactly is and is not a violation, but the most common form you’ll see is strings of dots (or arrows in C++) where you’re walking an object graph: Property.NestedProperty.NestedNestedProperty.You.Get.The.Idea. (It is worth mentioning that the existence of multiple dots is not always a violation of the Law of Demeter — fluent interfaces in general and Linq in the C# world specifically are counterexamples). It’s when you’re given some object instance and you go picking through its innards to find what you’re looking for.

One of the most immediately memorable ways of thinking about why this is problematic is to consider what happens when you’re at the grocery store buying groceries. When the clerk tells you that the total is $86.28, you swipe your Visa. What you don’t do is wordlessly hand him your wallet. What you definitely don’t do is take off your pants and hand those over so that he can find your wallet. Consider the following code, bearing in mind that example:

public class HardToTest
{
    public string PrepareSsnMessage(CustomerOrder order)
    {
        return "Social Security number is " + order.Customer.PersonalInfo.Ssn;
    }
}

The method in this class just prepends an explanatory string to a social security number. So why on earth do I need something called a customer order? That’s crazy — as crazy as handing the store clerk your pants. And from a testing perspective, this is a real headache. In order to test this method, I have to create a customer, then create an order and hand that to the customer, then create a personal info object and hand that to the customer’s order, and then create an SSN and hand that to the customer’s order’s personal info. And that’s if everything goes well. What if one of those classes — say, Customer — invokes a singleton in its constructor. Well, now I can’t test the “PrepareSsnMessage” in HardToTest because the Customer class uses a singleton. That’s absolutely insane.

Let’s try this instead:

public class HardToTest
{
    public string PrepareSsnMessage(string ssn)
    {
        return "Social Security number is " + ssn;
    }
}

Ah, now that’s easy to test. And we can test it even if the Customer class is doing weird, untestable things because those things aren’t our problem. What about clients, though? They’re used to passing customer orders in, not SSNs. Well, tough — we’re making this class testable. They know about customer order and they its SSN, so let them incur the Law of Demeter violation and figure out how to clean it up. You can only make your code testable one class at a time. That class and its Law of Demeter violation is tomorrow’s project.

When it comes to testing, the more stuff your code knows about, the more setup and potential problems you have. If you don’t test your code, it’s easy to write train wrecks like the “before” method in this section without really considering the ramifications of what you’re doing. The unit tests force you to think about it — “man, this method is a huge hassle to test because problems in classes I don’t even care about are preventing me from testing!” Guess what. That’s a design smell. Problems in weird classes you don’t care about aren’t just impacting your tests — they’re also impacting your class under test, in production, when things go wrong and when you’re trying to debug.

Understand the significance of polymorphism for testing

I’ll leave off with a segue into the next chapter in the series, which is going to be about a concept called “test doubles.” I will explain that concept then and address a significant barrier that you’re probably starting to bump into in your testing travels. But that isn’t my purpose here. For now I’ll just say that you should understand the attraction of using polymorphic code for testing.

Consider the following code:

public class Customer
{
    public string FirstName { get { return TestabilityKiller.Instance.GoGetCustomerFirstNameFromTheDatabase(); } }
}

public class CustomerPropertyFormatter
{
    public string PrepareFirstNameMessage(Customer customer)
    {
        return "Customer first name is " + customer.FirstName;
    }
}

Here you have a class, CustomerPropertyFormatter, that should be pretty easy to test. I mean, it just takes a customer and accesses some string property on it for formatting purposes. But when you actually write a test for this, everything goes wrong. You create a customer to give to your method and your test blows up because of singletons and databases and whatnot. You can write a test with a null argument and amend this code to handle null gracefully, but that’s about it.

But, never fear — polymorphism to the rescue. If you make a relatively small modification to the Customer class, you set yourself up nicely. All you have to do is make the FirstName property virtual. Once you’ve done that, here’s a unit test that you can write:

public class DummyCustomer : Customer
{
    private string _firstName;
    public override string FirstName { get { return _firstName; } }

    /// 

    /// Initializes a new instance of the DummyCustomer class.
    ///

public DummyCustomer(string firstName) { _firstName = firstName; } } [TestMethod, Owner(“ebd”), TestCategory(“Proven”), TestCategory(“Unit”)] public void Adds_Text_To_FirstName() { string firstName = “Erik”; var customer = new DummyCustomer(firstName); var formatter = new CustomerPropertyFormatter(); Assert.IsTrue(formatter.PrepareFirstNameMessage(customer).Contains(firstName)); }

Notice that there is a class, DummyCustomer declared inside of the test class that inherits from the Customer class. DummyCustomer is an example of a test double. You’ll notice that I’ve created a scenario here where I define a version of FirstName that I can control — a benign version, if you will. I effectively bypass that database-singleton thing and create a version of the class that exists only in the test project and allows me to substitute a simple, friendly value that I can test against.

As I said, I’ll dive much more into test doubles next time, but for the time being, understand the power of polymorphism for testability. If the legacy code has methods in it that are hard to use, you can create much more testable situations by the use of interface implementation, inheritance, and the virtual keyword. Conversely, you can make testing a nightmare by using keywords like final and sealed (Java and C# respectively). There are valid reasons to use these, but if you want a testable code base, you should favor liberal support of inheritance and interface implementation.

A Note of Caution

In the sections above, I’ve talked about refactorings that you can do on legacy code bases and mentioned that there is some risk associated with doing so. It is up to you to assess the level of risk of touching your legacy code, but know that any changes you make to legacy code without first instrumenting unit tests can be breaking changes, even small ones guided by automated refactoring tools. There are ways to ‘cheat’ and tips and techniques to get a method under test before you refactor it, such as temporarily making private fields public or local variables into public fields. The Michael Feathers book below talks extensively about these techniques to truly minimize the risk.

The techniques that I’m suggesting here would be ones that I’d typically undertake when requirements changes or bugs were forcing me to make a bunch of changes to the legacy code anyway, and the business understood and was willing to undertake the risk of changing it. I tend to refactor opportunistically like that. What you do is really up to your discretion, but I don’t want to be responsible for you doing some rogue refactoring and torpedoing your production code because you thought it was safe. Changing untested legacy code is never safe, and it’s important for you to understand the risks.

More Information

As mentioned earlier, here are some excellent resources for more information on working with and testing legacy code bases:

And, of course, you can check out my book about unit testing: Starting to Unit Test, Not as Hard as You Think.

By

Introduction to Unit Testing Part 4: Design New Code For Testability

In the last post in this series, I covered what I think of as an important yet seldom discussed subject: how not to overwhelm yourself and get discouraged when you’re starting to unit test. In the post before that, I showed the basics of writing a unit test. But this leaves something of a gap. You can now write a unit test in a vacuum for an extremely simple class, and, when looking at your legacy code base, you know what to avoid. But you don’t necessarily how to write a non-trivial application with unit tests.

You might understand now how to write tests, assuming that you have some disconnected new class in your code base without dependencies and barriers to testing, but you wonder how to get to that point in the first place. I mean, your application doesn’t seem to need a prime number finder or a bowling score calculator. It needs you to add lines of code to existing methods or new methods to existing classes. It doesn’t really seem to need new classes, so the initial momentum and resolution you’ve built reading these first three posts to go and be a unit tester sort of fizzles anticlimactically when you stare at your code base.

What’s going on here?

Recognizing Inhospitable Terrain

The first thing to understand is that your code base probably wasn’t written with testability in mind. There’s nothing wrong with you for not being able to see where unit testing fits in, because it doesn’t. Last time I talked about things you’ll see that will torpedo your efforts to test a particular method or piece of code, but let me speak to some entire architectures and patterns that don’t lend themselves to testability. It’s not that code written with these technologies and patterns can’t be tested–it’s just that it won’t be easy for you. As you read through this, you might feel like I’ve read your code base’s mind.

  1. Active Record as an architectural pattern
    This is a pattern in which you create classes that are in-memory representations of database tables, views, or stored procedures. If you see in your code base a class called “Customer” that has methods like “GetById()”, “Update()” and “MoveNext()” you’ve got yourself an Active Record architecture. This architecture tightly couples your database to your domain logic and your domain logic to the rules for navigating through domain objects. You can’t test any of these objects since any operation you perform on them sends them scurrying off to create database connections and parameterized queries and all manner of other untestable stuff. And since decomposition and decoupling is the path toward unit testing, this sort of tight coupling of everything in your code is the path away from it.
  2. Winforms
    Winforms in the .NET world are tried and true when it comes to rapidly cranking out functional little applications, but you have to work really, really hard to make code that uses them testable. Q&A sites are littered with people trying to understand how to make Winforms testable, which should tell you that making them testable is not trivial. If you have Winforms and Active Record both in the same code base, at least the architecture is split into two concerns. But it’s split into two thoroughly untestable ones.
  3. Webforms
    See Winforms. Webforms is very similar in terms of framework testability, and for pretty similar reasons. Webforms is arguably even harder to test, however, because it’s predicated on spewing out reams and reams of HTML, CSS, and Javascript while allowing you to pretend you’re writing a desktop app. I’ve talked about my opinion of this technology before.
  4. Wizard/markup-reliant code
    Do you use the Webforms grid wizard thing to generate your grids? Do you define object data sources, such as DB connections or files, in the markup? Practices like these are the epitome of quick and dirty, rapid-prototyping implementations that hopelessly cross couple your applications beyond all testability. If this is something that’s done in your group/code base, testing is basically a non-starter until you go in a different architectural direction.
  5. Everything in your application is in a user control/form
    I’ve seen this called “Smart UI” and it basically means that there’s absolutely no separation of concerns in your code. The UI elements create database connections, write to files, implement business rules–they do everything. Code like this is impossible to unit test.

If any of this is sounding familiar, your task might be daunting. I have my own preferences, but I’m trying not to offer a value judgment here as much as I’m letting you know what you’re up against. I’m like a mortgage broker that’s saying to you, “if you want to own a home, that’s a great goal. But if you are eight months behind on your rent and have no personal savings, you’re going to have some work to do first.” If I’ve described your code base in the list above, you face different challenges than a green field developer. And since you’ve presumably been contributing to these code bases, you’re probably very used to implementation techniques that don’t result in testable code. You’re going to need to change your thinking and your coding practices in order to start writing testable code.

Once we’ve discussed how to get you writing testable code, I’ll come back to these macroscopic concerns and give some pointers for how to improve the situation. But, for now, on to a revised approach to coding. The following holds true whether you’re banging away at some legacy Winforms/Active Record application or starting a brand new MVC 4 site.

Add New Methods and Classes First, Ask Questions Later

First thing to abide by is to favor adding new things to the code over modifying existing things in the code. You may have heard this before in the context of the “Open/Closed Principle”, but that’s a guide for how to write your classes. (Basically, it admonishes you to write classes that others can extend and override rather than change.) I’m talking about how to deal with existing code bases. To put it simply and bluntly, it’s a lot easier to both code and test brand spankin’ new classes than to write and test changes to existing ones. We all know this. It’s at the heart of why we as developers always lean toward rewriting others’ code instead of understanding and working with it.

Now, this might not win you friends. In shops where people tend to write procedural code (you know, the kind you’re trying to get away from writing), they seem to have some weird fear of creating too many classes and masochistic attachment to monolithic structures. You might have to compromise or practice on your own if you run afoul of the project’s architect, but the exercise is invaluable. It’s going to propel you toward decoupling as a default rather than an exception. Doing new things? Time for a new class and some unit tests.

I know what you’re thinking: “But what if the thing that needs to be done has to be done in the middle of some method somewhere?” Well, instantiate your new class at that point and use it. “But what if it needs a bunch of fields from the class it’s in and variables from the method?” Pass them in through the constructor or method call. “But won’t that make my design bad?” It already is bad, but at least now you’re making part of it testable. When your code is testable and under test, everything is easier to fix later.

You’ll have to use some discretion, obviously, but shift your attitude here. Don’t look at a project that has nothing but .aspx files and their code behind and hide classes in there for fear of breaking with tradition. Boldly add pure .cs files to the code base. Add unit tests for those .cs files you’re creating. (Apologies to Java readers, but this has no real Java equivalent that I can think of having used. Plus, the Java stack in general seems not to have the same level of untestable cruft built into frameworks.) It’s a lot harder for someone, even the project architect, to give you a hard time if your new way of doing things is covered by unit tests. Even if they’re hostile Expert Beginners, they’re hard pressed not to sound silly if they say, “we don’t do that here.” You’ll at least have a better chance of pushing this change through with the unit tests than without them.

Ask Questions in the Right Order: What, How, When

Now that you’re creating a lot of new classes and instantiating them in the old untestable ones, it’s time to start working on what kind of code you write. If you’ve practiced and come back, I suspect that you’re starting to be able to write a few useful tests but are perhaps still struggling. And I bet it’s because the line between where the old class ends and your new one begins is a little hazy. Maybe they share some common fields. Maybe when you instantiate the class you’re testing, you hand it a “this” reference so that it can go picking through the properties on the untestable behemoth from which you’re escaping. This is the next thing we need to tighten up–stop doing that. A clear, concise division of labor between the classes is necessary, and it’s not possible if they share all of the same fields, properties, state, etc. That’s like a break up where you two continue to live together, share a car, and go to the movies on weekends.

The best way to achieve this clean split is with good abstraction, and the best way to do that is to remember “what, how, when.” When it’s time to change the code base, remember that you want to favor creating a new class. But before you do that, ask yourself “what?” but not the other two questions. What should I name this class? What should it do? What should it expose as its public methods? Don’t start thinking about how those methods or classes should work and don’t you dare start thinking about when anything at all should happen–just think about what. Give it a good name that defines a clear purpose, and then give it a good set of methods and properties that draw attention to why it’s a different concept than the class that will be using it. Ask yourself what the boundaries between the two classes will be so that you can minimize the amount of shared information. And now, start stubbing out those methods with no implementation and start stubbing out some test methods with names that say what the methods will do.

At this point, you’re ready for “how.” Start actually implementing the “what” and testing that your implementation works in the unit tests. Believe me, it’s much, much easier to implement methods this way. If you think about “what” and “how” at the same time, you start writing confusing code that not even you have faith in when you’re done. Implementation alone is much easier when you have a clear picture of “what,” and unit testing is a breeze.

Once everything is implemented, you can start thinking about “when.” When should you instantiate the class and when should you call its methods? But don’t spend too much time with “when” in your head because it’s dangerous. Does that sound weird? Let me explain.

“When Code” is Monolithic Code

Picture two methods. One is a 700 line juggernaut with more control flow statements than you can keep track of without a spreadsheet. The other is five lines long, consisting only of an initialize statement, a foreach, and a return statement. With which would you rather work? I imagine the response is unanimous here. Even if you tend to crank out these kinds of large methods, when you step through the debugger, looking for the cause of a bug and finding yourself in some huge method, your heart sinks and you settle in with snacks and caffeine because it’s going to be a long day.

Now with these two methods in mind, imagine if we were pair programming together and I simply asked “when?” With the tiny method, you’d probably say, “What do you mean by ‘when’–I mean, you initialize before the loop and you return when you find the record you’re looking for. What a weird question!” With the other method, you’d probably affect a thousand-yard stare and say, “Man, I don’t even know where to begin.” But the answer to that question would fill pages. Books. Because you set the first loop counter j equal to the third loop counter k about twenty lines before the fourth try-catch and fifteen lines before you set the middle loop counter j equal to four. Unless, of course, you threw that exception up on line 2090, in which case j might never have been initialized. Er, wait, I think that happened somewhere near the fifth while loop in that else condition up there. Oh, there’s so much “when,” but it’s all slammed together in a method where you can’t possibly test any of it. Lots of thinking about “when” breeds huge methods like a Petri dish for bacteria.

“When” code is procedural at its core, and procedural, “when” code is an anathema to object-oriented unit testing, which is all about “what” and “how.” Remember earlier in the series when I said that multi-threaded code was really, really hard to test? Well, that’s just a subset of an idea called “temporal coupling,” and what we’re talking about here also falls under that umbrella. Temporal coupling is what happens when things have to be executed in a specific order or else they do not work.

Imagine that you’re coding up a model for someone’s day. When you think of how to do this, do you think, “first he gets up, then he brushes his teeth, then he showers, then he puts on his clothes, then… then he comes home, then he eats dinner, then he watches TV, then he goes to bed?” Do you code this up with constructs like:

public void GoAboutMyDay()
{
    bool wokeUp = WakeUp();
    if (wokeUp == true)
    {
        bool showered = Shower();
        if (showered == true)
        {
            bool putOnClothes = PutOnClothes();
            if(putOnClothes)
                //You get the idea
        }
    }
}

This method is all about “when.” It’s entirely procedural, and it’s going to be horrifying when it’s complete. When you get into the fortieth nested if condition, maybe someone will come along and flatten it out with a bunch of inverted early returns. Or maybe not, because maybe some of if clauses start sprouting else conditions with loops in them. And maybe the methods being called start communicating with one another via boolean flag fields in the class. Who knows–this thing is on the precipice of becoming unstoppable. It might just achieve sentience at some point, so that when you try to start deleting conditionals, it says, “I can’t let you do that, Dave,” and puts them back.

The root problem behind it all is the “when” and the procedural thinking because you’re orienting the implementation around the order of the activities rather than the nature of the activities. Unit testing is all about deconstructing things into their smallest possible chunks and asserting things about those chunks. Temporal coupling and “when” logic is all about chaining and fusing things together.

If you were thinking about “what” first here, you would form a much different mental model of a person’s day. You’d say things to yourself like, “well, during the course of a person’s day, he probably wakes up, gets dressed, eats breakfast–well, actually eats one or more meals–maybe works if it’s a weekday, goes to bed at some point,” etc. Whereas in the procedural “when” modeling you were necessarily building a juggernaut method, here you’re dreaming up the names of methods and/or classes that can be unit tested separately and in isolation. It’s no reach to say, “okay, let’s have a Meal class that will have the following methods…”

Only at the end will you decide “when.” You’ll decide it after you’ve stubbed things out with the “what” and implemented/unit tested them with the “how.” “When” is a detail that you should allow yourself to figure out at any point down the line. If you nail down what and how, you will have testable, modular, and manageable code as you create your classes.

Other Design Considerations for Your New Classes

I’ll wrap up here with a few additional tips for creating testable designs when adding code-to-code bases:

  1. Avoid using fields to communicate between your methods by setting flags and tracking state. Favor having methods that can be executed at any time and in any order.
  2. Don’t instantiate things in your constructor. Favor passing them in (we’ll talk about this in detail in a future post in the series).
  3. Similarly, don’t have a lot of code or do a lot of work in your constructor. This will make your class painful to setup for test.
  4. In your methods, accept parameters that are as decomposed as possible. For instance, don’t accept a Customer object if all you do with it is read its SSN property. In that case, just ask for the SSN.
  5. Avoid writing public static methods. These are easy enough to test (often), but they start introducing testability problems when you write code that uses them. (This might be hard to swallow at first, but mull over the idea of simply not using static methods anymore.)
  6. The earlier you start writing your unit tests, the better. If you find that you’re having a hard time testing your new code, it’s more likely a problem with the code than with unit testing it, and if you write tests early, you’ll discover these problems before you get too far and fix them.

This post has covered ways to write unit tests “from here forward” and ways to stop adding untested code to code bases. In the next post, I’ll talk about how to start getting the legacy code under test.

Addendum: Mitigating the Hostile Test Environments

Finally, as promised, here are ways to accommodate testing in the less-than-ideal architectures mentioned above, if you’re curious or want to do some more research:

  1. Instead of Active Record, look at some kind of ORM solution like NHibernate or Entity Framework. These are tools that generate all of the code for you to access the database so that you don’t have to worry about testing that code and you can focus on writing only your (testable) domain code. Barring that, try to separate the three concerns of Active Record objects: modeling the database, connecting to the database, and modeling a domain object. The first concern adds no value, and the second two can be broken out into separate objects where the only thing hard to test is the actual database access.
  2. Instead of Winforms, favor WPF when possible. If that isn’t possible, see if you can use the Model-View-Presenter (MVP) pattern to move as much logic out of the untestable code-behind as possible.
  3. To be blunt, from a testing/decoupling perspective, Webforms is a disaster. You can have some limited success by adopting a more passive binding model and moving as much code out of the code-behind as possible, but it’s all pretty awkward. Webforms really seems more about rapid-prototyping and Microsoft-Accessing web development than producing scalable, sophisticated architectures.
  4. If you’re using wizards to generate your application’s architecture, cut it out. If you’re defining implementation details in markup, cut it out. Markup is for layout, not unit-testable business logic or state logic. If you depend on definitions in markup to drive your application’s behavior, you’re relying exorbitantly on a third-party framework, which is always extremely brittle from a testability perspective.
  5. To fix Smart UI, you just have to factor toward a more decoupled architecture. Start pulling different concerns out of the user controls and forms and finding a home for them.