Sunday, September 15, 2013

TDDing to ILock

My guiding star: when faced with a difficult TDD problem, refactor to make it easy. There are many techniques for doing so (I only know a handful).

What if we use dependency injection?

1. Wrap the `lock` keyword in a custom class.
2. Pass in that custom object
3. In tests, pass a mock of that custom object instead.

Here's the resulting implementation of Counter:

    class Counter
        readonly ILock @lock;
        public Counter(ILock @lock)
            this.@lock = @lock;
        internal int MoveNext()
            using (this.@lock.Acquire())
                return CurrentValue++;
        public int CurrentValue { get; private set; }

One way of looking at what I've done here is addressed the Primitive Obsession around the _syncRoot object in the conventional pattern. You can't get much more primitive than System.Object!

Note that I explicitly called out which steps were RED, GREEN, and REFACTOR. 

5b36285 REFACTOR; Rename `MyLock` -> `Lock`
c149f15 REFACTOR: Rename `Lock()` -> `Acquire()`
66518d7 GREEN: Simple `Lock()` and `IsLocked`
9b27a8c RED: LockedLockShouldBeLocked

Also, as the code currently stands (, there are 3 implementations of ILock:

MonitorLock, which attempts to duplicate the behavior of the `lock` keyword in C#, according to the language specification.
MockLock, which makes it easy to confirm that code is locking as expected.

SingleThreadedLock, which actually doesn't lock at all.

I created the different Lock implementations with TDD. All 3 pass the exact same set of unit tests.

Fun stuff.

No comments: