Instrument Your Tests

Here is the first of seven blog posts based on the section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software, called Seven Strategies for Using Tests as Specifications. In this first post, I’ll discuss a technique called instrumentation that I learned from Scott Bain and Amir Kolsky at Net Objectives.

Instrumentation is a technique for writing your tests so that they’re more readable and understandable as specifications. We do this by replacing magic numbers and other values with constants or fields. This gives us an opportunity to assign meaningful names to the values we use to exercise the behaviors that we are testing, making our tests more readable.

For example, instead of doing this:

 @Test

   public void testConstructor() {

   User user = new User(“Clark”, “Kent”, “user@example.com”, 

“Superman”, “kryptonite”);

     assertEquals(“Clark”, user.firstName());

     assertEquals(“Kent”, user.lastName());

     assertEquals(“user@example.com”, user.eMail());

     assertEquals(“Superman”, user.userName());

     assertEquals(“kryptonite”, user.password());

   }

We do this:

 @Test

   public void testRetrievingParametersAfterConstruction() {

     String firstName = “Clark”;

     String lastName = “Kent”;

     String eMail = “user@example.com”;

     String userName = “Superman”;

     String password = “kryptonite”;

     User user = new User(firstName, lastName, eMail, userName, password);

     assertEquals(firstName, user.firstName());

     assertEquals(lastName, user.lastName());

     assertEquals(eMail, user.eMail());

     assertEquals(userName, user.userName());

     assertEquals(password, user.password());

   }

This makes the test more readable and disambiguates what it is we are actually specifying. In the first example, there are lots of redundant strings that the compiler can’t check for consistency, so if a field is misspelled, it can’t be caught by the compiler. It also makes it hard to understand the meaning of the fields with just the contents of the strings. The second example is clearer. It makes the test read like a specification so it’s clear what the code does. Instrumentation is one of my favorite techniques for helping me write clear and understandable unit tests that read like specifications.

I find that the discipline of thinking about the behaviors that I want to create before I think about how I want to implement those behaviors has helped me build better systems. As a result, the code I write is more partitioned and independently verifiable. I find that the technique of instrumenting my tests is valuable for making it clear what my code is doing. 

As a result, I find that I have to write far less developer documentation because the way to use my APIs are how my tests consume them. With automated tests in place, I never have to run code through the debugger to verify that it works.

I find that one of the biggest challenges that developers face when adopting the test-first development methodology is knowing what tests are the correct tests to write. But if we think about our tests as a form of eliciting the behavior that we want to create, then it helps us build the right stuff and get immediate feedback that it works.

Instrumentation is a little extra effort but it gives me a great deal of value and allows me to turn an activity that I’m already doing, which is test-first development, into another activity—creating an executable specification for my code. I also get a suite of regression tests that validate that my features are written at the right level of abstraction to support me in refactoring my code later. To me, those are some pretty big benefits.

Note: This blog post is based on one of the “Seven Strategies…” sections in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.

To view or add a comment, sign in

More articles by David Scott Bernstein

  • Green Tests and Red Tests

    In practice, I found that there are times that I want a bit more test coverage than I get from just doing test-first…

  • Radiators and Silos

    In the old days of corporate America, the way you got ahead was through hard work and perseverance. You strove to…

    2 Comments
  • Makers and Menders

    I’ve been getting back into some research interests of mine that require data acquisition from a variety of sensors so…

  • Core Developer Practices

    Every field of engineering has a core set of practices that they follow and software engineering is no different. But…

    1 Comment
  • Still XP After All These Years

    Are you humming in your head Paul Simon’s “Still Crazy After All These Years”? I am. And it does seem crazy.

  • The Importance of Continuous Integration

    Perhaps the most important yet easiest to implement of all the software development practices in Agile is continuous…

  • The Importance of Technical Practices (Again)

    Software development has undergone many revolutions over the last few decades. The way we build software today is…

  • Summary of Seven Strategies Series

    I finished my “Seven Strategies” series of 72 blog posts with seven strategies for implementing each of the nine…

    1 Comment
  • Refactor to Learn What Not to Do

    One of the things that I was not expecting when I started refactoring other people’s code was that I started to see…

    4 Comments
  • Refactor to Clean Up Before Moving On

    Of course, the best time to refactor code is while it’s fresh in your mind, right after having worked with it. Once I…

Others also viewed

Explore content categories