Story Telling with Programmer Tests

(First test post with Windows Live Writer Beta…)

Here are the tests for a class I wrote a while ago. This class is used internally by the Caching Application Block inside the Enterprise Library. It has a bunch of housekeeping data inside of it, but the only really interesting behavior in it revolves around determining when an item in the cache is expired. The user is allowed to provide zero or more objects who can determine, in a manner of their own choosing, whether or not the item in question has aged too long and should be flushed from the cache.

What I hope to show, and what I’d love feedback on, is whether or not these tests tell you the story of this class. This test class is named CacheItemExpirationsFixture, which I hope gives you, as the programmer and reader, a clue that I’m going to be talking about CacheItem’s and how they are expired. And I truly hope that the names of the tests tell a progressive story about how this class was designed, what my intention was when I wrote each of the test methods, and would give someone else enough of a clue to be able to figure out which test to look at to learn something of interest about the class under test. 

namespace Microsoft.Practices.EnterpriseLibrary.Caching.Tests
{
    [TestClass]
    public class CacheItemExpirationsFixture
    {
    [TestMethod]
        public void CanExpireCacheItemFromSingleExpirationObject()
   [TestMethod]
        public void ItemShouldNotBeExpiredIfNoExpirationObjectIsExpired()

    [TestMethod]
        public void ItemNotExpiredIfAllExpirationsAreFalse()

    [TestMethod]
        public void ItemIsExpiredIfAllExpirationsAreTrue()

    [TestMethod]
        public void ItemIsExpiredIfAnyExpirationsAreTrue()

    [TestMethod]
        public void ItemIsNotExpiredIfNoExpirationsAreGiven()

    [TestMethod]
        public void ItemIsNotExpiredIfEmptyExpirationArrayIsGiven()

    [TestMethod]
        public void SingleExpirationIsNotifiedWhenCacheItemUpdated()

    [TestMethod]
        public void AllExpirationsNotifiedWhenCacheItemUpdated()

    [TestMethod]
        public void CtorForLoadingInitializesExpirationsToMatchLastAccessedTime()
    }
}

Now here is the body of one of the tests. It is short, declarative, and illustrative of the intent I had in mind as I wrote this test. All the tests in this class are similar to this, differing mostly in the details of what was being tested.

 

 
    [TestMethod]
        public void ItemIsExpiredIfAnyExpirationsAreTrue()
        {
            CacheItem cacheItem = new CacheItem("key", "value", CacheItemPriority.Normal, null, new NeverExpired(), new AlwaysExpired());
            Assert.IsTrue(cacheItem.HasExpired(),
                          "Any expirations in collection are expired, cache object is expired");
        }

Now, its entirely possible that some of the details of the context might not be familiar to you, but hopefully the basic idea is. I create my object, call one of its methods, and ensure that the right thing has happened. The test is short, complete in and of itself, and hopefully pretty descriptive of the what I was trying to demonstrate.

The reason I’m writing this is that I’ve been seeing a lot of tests lately that do none of the things that I’ve just written about. They have a generically written test fixture name, the names of each individual test method are short, like public void incrementCount(), and the implementation of the test is long, twisted, complicated, and totally unrevealing of the intent of the person writing the test.

I’m hoping that this post can serve as a jumping off point for me to start blogging about what it takes to keep tests telling simple stories, and for having the tests drive a simple, yet complete, design into the application code. I intend this to be a continuing series on this topic, as I feel quite strongly about it. In my mind, the ability to create simple tests, have those tests tell a compelling story, and have the test methods themselves be an expression of my design intent, rather than an example of a messy implementation, is the key to really getting TDD.

More (much more) later.

— bab

Eating Toast

There are two ways to break down a project (OK, there are probably more than two ways, but work with me here…)

One way is horizontally, meaning to implement an entire application layer at once. This would be like building the entire UI first, then the business layer, then the database, etc. The other way is to implement bits and pieces of all layers over and over, which is like taking a vertical slice through a program — a bit of UI, business logic, persistence, and so on. When people are first learning Agile, it seems to be difficult to get their heads around what taking a vertical slice actually means and why they would want to do it. They need everything built before anyone would possibly want to use their creation, so vertical versus horizontal doesn’t make any practical difference to them. It becomes my job to explain why vertical is superior in most casts. So I’m talking to a manager here, trying to find the right words to allow him to understand what I’m trying to say, and I stumble upon this metaphor, Eating Toast.

Let’s say we’re going to hold a dinner party and we’re responsible for preparing the feast. This act consists of 4 separate activities: Shopping, Cooking, Eating, and Cleaning. If I were to implement this task horizontally, I’d learn how to shop really, really, really well, and fill up my grocery cart as well as anyone else in the entire world. But, who cares? I can’t do anything with the food, so I’m left with this pile of food and no way to make use of it. Ah, so now I need to learn to cook. Now I can take the food I bought and do something with it — I can cook it! But, again, who cares? Having plates full of cooked food is not useful to anyone if you don’t know how to eat it. And the story goes on, through the eating and cleaning phases of our dinner project/party.

Now, an alternative way of looking at this is that, instead of learning how to be the world’s best shopper, cook, eater, and cleaner before I can actually have my party, maybe I can learn how to shop for, prepare, eat, and clean up from a simple meal of a piece of toast. If I want to throw a Toast-Only Dinner Party, I’m now set  I’ve learned how to buy, cook, eat, and clean toast. Next, I can learn how to cook waffles, followed by sandwiches, etc, each time making my potential dinner party more lavish and elegant. Dish by dish, I can learn how to expand my knowledge beyond Eating Toast.

After hearing this metaphor, the idea of vertical versus horizontal made sense. Does it make sense to you?

Yours, with crumbs,

bab