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.