What Is Live Unit Testing? Running Your Tests Automatically

by Hit Subscribe 4. January 2019 03:36

Let's say that you've given unit testing a try. Not only that, but you've even decided to go all in and try test-driven development. After reading all about the red-green-refactor cycle, you're now staring at your empty IDE just waiting for inspiration to strike.

Then one of your co-workers mentions she's starting to use live unit testing and she really likes it. Should you try it as well?

Let's spend some time together exploring what live unit testing is.

live unit testing

First, What's Plain Old Unit Testing?

Before we look at live unit testing, let's quickly review the concept of unit testing. A unit test is a stand-alone method that exercises a portion of your code, in a way that you define, in order to measure the result. The amount of code that a unit test runs depends upon how you define what a unit is. However, there's a trade-off between larger units of code and test failure troubleshooting. (Did it fail because my validation logic bombed? Or did I build the data transfer object incorrectly? Did my authorization logic fail?) If you're unconvinced of the benefits of unit testing, I suggest checking out our post on why unit testing is important.

Arguments about unit test size aside, as you write more unit tests, you build a collection of executable declarations of your code's behavior. As you continue adding to and refactoring the existing code, running all the unit tests ensures the existing behavior defined by those tests remains unchanged. This is by no means an exhaustive look at unit testing, but it's enough to get us started talking about live unit testing.

So What Then Is Live Unit Testing?

Once you have a suite of unit tests available, you then begin to enjoy the benefit of these tests as you make changes to the code.

If you follow the traditional test-driven development (TDD) practice of red-green-refactor, you'll write a new unit test that fails. Next, you'll run the test suite (or perhaps a subset of the suite) to see the new test fail. Then you'll add code to make the test pass. When you run the tests again, you'll watch the new unit test pass. And you'll then proceed to do any refactoring which your initial solution requires. By running the tests a third time, you'll ensure the code is still in a good ("green") state.

Within that single cycle of red-green-refactor, you'll have had to run the test suite three times. It's that repeated test running which live unit testing aims to eliminate.

In the scenario above, the workflow could be divided into two general phases—coding and running tests. Live unit testing helps to keep you in the coding phase longer. Since your tests are built and run continuously, you'll just pause after writing your initial failing test and watch a failing test indicator appear within your IDE. Once it appears, you'll then switch over to your production code to write the simple solution to make the new test pass. As you finish the simple solution to make the test pass, you'll watch the red failing test indicator turn green. You're now in the position of being able to refactor the simple solution armed with the confidence that your changes are covered by your growing unit test suite. Live unit testing has helped keep you in the flow of coding longer by eliminating one of the more tedious parts of test-driven development.

Screenshot of Visual Studio with NCrunch running showing passing test indicators

The green passing-test indicators along the left side of the IDE show the results of live unit testing.

Is Live Unit Testing Just About Saving a Few Seconds?

"OK," you think to yourself. "I think I get it. But I'm pretty quick with the keyboard shortcut to trigger a test run. Why should I try out live unit testing?"

I would encourage you to not underestimate the importance of staying within the coding phase of your workflow. Ours is an industry in which interruptions are common. Unless you've taken steps to reduce them, your workday will be filled with notifications from your email, Slack, Webex, and more. With all of these things vying for our attention, it's easy to switch over to something else once you've hit the shortcut key to run tests. "This will take a few seconds," you think to yourself. "Maybe I'll just see if Andy responded to my earlier email."

Well, Andy did respond to your earlier email, but he didn't understand what you were asking. You dash off a quick reply to Andy, copying Phyllis on the email to ensure that she can give Andy exactly the information he needs. Just a few moments after the email has left your outbox, Phyllis hits you up on Slack to ask about the message you just sent. You spend a few minutes chatting with her, and together you decide on what Andy needs so that he can answer your original question in the first email. OK, now where were we?

Let's now switch back to the IDE. What were we doing? Let's try building the solution to see if we were in the fail phase. Oh, the solution was already built. That's right; we were about to run the tests. You hit the shortcut key to run the tests and wait (again) for them to finish.

In addition to the time you lost to repeated runs of the build and tests, you lost the time needed to context switch back to the code. Little attention-sapping interactions like these can happen throughout the day. Having breaks between the coding/running tests phases of your workflow can make you more susceptible to these interactions. The longer you can stay within the coding flow phase of your workflow, the more productive you will feel.

How Else Can Live Unit Testing Help?

All code projects start out with only the best of intentions. Every method will be concise and single-purpose. The classes will be short and only have one reason to change. Those of us who have worked on the dreaded "legacy project" know that projects quickly become messy. Despite the best of intentions, classes begin to reference other classes in unexpected and exciting ways. That unintentional coupling can be a subtle source of bugs. Can live unit testing help here?

Recall the helpful green/red test indicators, which tools like NCrunch add to your IDE. As you work, you watch those indicators turn from green to red and back again. Throughout your day, you get used to seeing that pattern as you refactor your code. Let's walk through a scenario in which the indicators don't exactly behave as you expect.

Live Unit Testing Locates an Architectural Concern

Let's say you're adding logic to an existing class that does validation of coupon codes. At your disposal is a suite of unit tests that help ensure your existing validation logic works. You then begin your work, seeing the test indicators go red as you add your new test. For this scenario, a validation failure results in an exception being thrown. You add that simple solution, throwing the exception. You then pause, waiting for your test indicators to turn green.

Hmm... They should have turned green by now. What's going on?

You run your entire unit test suite only to discover you have a failing unit test in another part of your solution that you weren't expecting. Apparently, another developer had set up that unit test to use the same validation class you were working on, and your new exception is causing that test to fail. This is a point of coupling within your code that you weren't expecting. Is this coupling necessary or even desired?

Regardless, you were made aware of it right at the time you introduced the change. You can examine the architecture and discuss it with your team to decide if this is something that should remain. Not only have you avoided the bug, but you now understand more about your application's architecture.

You've Convinced Me. What's Next?

Give live unit testing a shot with the project you're currently working on. Try it out while refactoring an existing class. If you practice TDD, try it out while in the red-green-refactor workflow. See if it brings you the productivity and workflow benefits mentioned here.

This post was written by Eric Olsson. Eric was first introduced to programming in high school, and he's been fascinated by it ever since. Most of his professional experience has been working with C# and other .NET technologies. After being introduced to agile methodologies and practices such as test-driven development, he's had a particular interest in applying them to the code he writes.

Tags:

Blog