Statics and unit testing

I was reading http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/ by Miško Hevery. He makes some excellent points about using statics and the effect they have on testing.

There seems to be a common misreading of the original article, where he says something like ‘if you use a static in a class, that class is harder to unit test’ and people hear ‘statics are hard to unit test’. This needs some addressing. This post seeks to explain why statics compromise testability with an example.

So, consider the development of a dictionary class with string keys. At some point you will want to hash the keys and assign the values to bins;

# pseudocode
class Dictionary:
    Add(key, item):
        hash = String.GetHashCode(key)
        bucketIndex = hash % maxBuckets
        ...
        this.buckets[bucketIndex].Add(item)

Now, a hash function (String.GetHashCode) is a perfect candidate for a static; it is a pure function with no state to worry about. It’s also easy to test. There are no worries about whether the static is testable.

But what about our dictionary class? Before I release this on the world, I want to convince myself that I’ve got the logic right for what happens when we get a collision — when two different keys generate the have the same hash code.

With the static sitting there in the Add function, I’m out of luck. How can I guarantee a collision in a test? I’d need to know two keys which generate the same hash code, and write a test like this;

    key1 = "2394820934802934820348"
    key2 = "lgjeibrieovmdofivevrij"
    dictionary = new Dictionary()
    dictionary.Add(key1, "A")
    dictionary.Add(key2, "B")

And here the dependency on the static becomes clear; in order to write a test for the dictionary, I need to know the implementation details of the static to write my test. I’ve made unit testing harder — remember, the unit is the dictionary.

So to make it testable, I need to add in a new idea into my Add method; the idea of a swappable object with a GetHashCode instance method;

# pseudocode
class Dictionary:
    Constuctor(hasher):
      this.hasher = hasher
    Add(key, item):
        hash = hasher.GetHashCode(key)
        bucketIndex = hash % maxBuckets
        ...
        this.buckets[bucketIndex].Add(item)

Now my test becomes trivial;

    class AlwaysCollidingHasher inherits Hasher
       GetHashCode(key):
           return 0 # this hasher always causes a hash collision

    dictionary = new Dictionary(new AlwaysCollidingHasher())
    dictionary.Add("A", "A")
    dictionary.Add("B", "B")

Now I have a proper unit test; my dictionary can now be tested reliably for this dangerous condition, and it’s no longer relying on a particular implementation detail of another class. It’s a true unit test.

So to reiterate; the point isn’t that statics are hard to test. The point is that sometimes, things that use statics are hard to test.

About these ads

4 thoughts on “Statics and unit testing

  1. The problem isn’t really the use of a static function though — it’s the use of a non-injected dependency. Your code could be revised to fix testability while still using static functions, by changing the first line of Add to `hash = hasher(key)`, then passing in the static function String.GetHashCode as the value of hasher.

  2. The problem isn’t really the use of a static function though — it’s the use of a non-injected dependency. Your code could be revised to fix testability while still using static functions, by changing the first line of Add to `hash = hasher(key)`, then passing in the static function String.GetHashCode as the value of hasher.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s