Net Objectives

Net Objectives
If you are interested in coaching or training in ATDD or TDD please click here.

Tuesday, October 21, 2014

TDD and Asychronous Behavior: Part 2



In  part 1, we discussed the benefits of separating out the code that ensures mutex (in this case, using thread locks) from the code that provides core behavior using a Synchronization Proxy.  The core behavior can be tested in a straightforward, single-threaded way.  What remains in terms of TDD and asynchronous behavior is how to effectively specify/test the Synchronization Proxy.

Testing the Synchronization Proxy


You might be saying “the proxy class is so simple, I’m not sure I’d need to drive its behavior from a specification/test.  All it does is take the lock and delegate” The level of rigor in your specifications is always a judgment call, so we’ll set aside whether a given proxy behavior needs a test. We’re going to focus on how to write such a test it in the case where you wish to. In other words: if you decide not to include it in the specification we want it to be because you decided not to, not because you didn’t know how.[3]

The given-when-then layout of the specification would be something along these lines:

Given:

    Threads A and B are running
    Thread A is running code T

When:

    Thread B attempts to run code T

Then:

    Thread B will wait until Thread A is done: the accesses with be serial, not parallel.

The key here is the word “until”. What the test needs to drive/specify/ensure is that the timing is right, that Thread B writes *after* Thread A even if Thread A takes a long time. Let’s look at an implementation sequence diagram.



Client A and Client B are inner classes of the test, created just to exercise the proxy, each in its own thread.  If the proxy did not add the synchronization behavior, the writes to Target would be 2, and then 1, because we tell the Target to wait 10 seconds before writing the state for Client A, but only 1 second for Client B.  If the proxy prevents this (proper behavior) then the writes will be 1, and then 2, because Client B couldn’t get the access until Client A was finished.

This is a partial solution, but it begs a few questions.
  1. How does the test get RealTarget to wait these different amounts of time?
  2. How does the test assert the sequence of these writes is 1, 2?
  3. If the RealTarget “waits 10 seconds” won’t the test execution be horribly slow?
The first two questions are answered by replacing RealTarget with a Mock Object[4]. Remember, we are not specifying RealTarget here, we are specifying the proxy’s behavior, therefore RealTarget must be controlled by the test. A mock allows this.

What about the time issue? Well, time is in scope and we certainly are not testing that time works. So we have to control it in the test as well.



Here’s the implementation sequence diagram with the mock object in place of RealTarget, and another object that replaces time.

Time is a simulator, which can be told by the test to “be” at any time we want. MockTarget basically calls on Time and says “let me know when we’ve reached or passed second x”. We use the Observer Pattern [5] to implement this. The first time the mock is called it will ask Time to notify it when 10 seconds have passed. The second time, it will ask for a 1 second notification. We do this with a simple conditional.

Furthermore MockTarget maintains a log of all calls made to it, in order, which the test can ask for to determine the sequence of setX() calls and assert that it is 1, 2 rather than 2, 1.

10 and 1 are not significant numbers, so as you’ll see in the code we made constants “longWait” and “shortWait” to be used in the test. It’s only important that the first thread waits longer than the second, and since time itself is being simulated anyway the “actual” lengths of time are unimportant. We can pretend they are one year and one hundred years if you want. It’s nice to control time. :)

MockTarget, Time, and the ClientA and ClientB objects are all part of the test, and so a good practice is to make them private inner classes of the test. Also the Observer interface and all constants used in this test are similarly part of the test itself. Remember, a test tests everything which is in scope but which the test does not control. The only thing not controlled by the test is the Synchronization Proxy.

We’ve coded all this up in C#. Click here to download the visual studio project.


[3] Perhaps later we’ll make our argument about whether you should or not. :)
[4] If you don't know about the Mock Object Pattern, visit this link:
http://www.netobjectives.com/PatternRepository/index.php?title=TheMockObjectPattern
[5] For more details on the Observer Pattern, visit this link:
http://www.netobjectives.com/PatternRepository/index.php?title=TheObserverPattern





TDD and Asychronous Behavior: Part 1


In TDD we write tests as specifications where each of them is focused on a single behavior of the system.  A “good” test in TDD makes a single, unique distinction about the system.  

But this means when the TDD process is complete our spec also serves as a suite of tests against regression.  This is a hugely valuable side-effect of TDD.  We don’t write “tests” but we get tests too, and with no additional effort.  As tests we also want each of them to be unique in another sense: each has only one reason to fail.  Thus when a test fails we will know exactly why.

We say it this way:  “A given test tests everything which is in scope, but which the test does not control.”  Clearly we never want to test “everything in scope” but rather one unique thing at a time.  The implication is that everything other than that one thing must be controlled by the test.

This can include many things… framework objects, libraries, the user interface, the database, time, randomness, devices and sensors, the network, etc…  For many of these entities we can solve the problem with the endo-testing technique [1], but one aspect of development can pose a special problem: multi-threaded execution.

Mutex

When a given behavior safely supports multiple threads it does so either because it is stateless/re-entrant, or it is using some mechanism to ensure mutual exclusion (“mutex”).  If the object is stateless or re-entrant then there are no thread-related issues to deal with in a test.  But if it is ensuring mutex as well then the mechanism it employs to do so is “in scope.”  When we are specifying the behavior of the object we don’t want to also specify the mutex-ensuring behavior in the same test.  Thus, we have to bring it under the control of the test.  This actually isn’t that hard as it might seem.  It’s a matter of technique.

Our first step is to separate the mutex behavior from the primary responsibility of the object.

Often objects will use thread locks in order to protect some functionality from being accessed by multiple threads simultaneously.  The problem is that these objects would be doing two things: providing the core functionality and managing the locks.  A given object should not be responsible for two things.  That’s a basic tenet of good design we call cohesion.  So the first step is to separate the two responsibilities, and one way to do this is by using a Synchronization Proxy.

The Synchronization Proxy

If we use a Synchronization Proxy [2] we can separate these two responsibilities into two different objects.  This means that the primary object will now only provide its core functionality (meaning we can specify its behavior it in a straightforward, single-threaded way) and the Synchronization Proxy will ensure the mutex.  We’ll explain the proxy first, and then of course we have to discuss how to specify its behavior in its own test.

Because we want to focus on techniques for specifying/testing the proxy part of this, the main object (which we are calling Target) will have an extremely simple behavior.  It has a bit of state that can be changed by calling a “Set” method.

public class Target {
    public int x;
    public override void SetX(int xValue) { x = xValue; }
}

Obviously this is a trivial class, and easy to specify in a test.  Remember, we’re not concerning ourselves with this class, but rather the proxy’s behavior that is going to ensure that two threads cannot call SetX() simultaneously or in an overlapping way.  First, we create an abstraction for this Target:

public abstract class Target {
    public abstract void SetX(int xValue);
}

public class RealTarget: Target {
    public int x;
    public override void SetX(int xValue) { x = xValue; }
}

Now RealTarget is an implementation of the Target abstraction.  Any client object will see only Target.  In a single-threaded system it could use RealTarget directly, but if multiple threads are to be supported we need to do more.  In that case we create a second implementation, which is the Synchronization Proxy.

public class SimpleLockSynchronizationProxy : Target {
    private RealTarget myTarget;
    public SimpleLockSynchronizationProxy(RealTarget aTarget) { myTarget = aTarget; }
    public override void SetX(int xValue) {
        lock (this) {
            myTarget.SetX(xValue);
        }
    }
}



Note that this proxy takes an instance of RealTarget it its constructor, and delegates to it for the core behavior (SetX()) which would be specified it its own (trivial) test. But the proxy takes the lock along the way, and thus guards against multiple, simultaneous, overlapping accesses. The proxy will not allow a thread to enter SetX() if another thread is currently executing it.

At run-time, we “string them together” and expose the Proxy to client objects, up-cast to Target. Note this make no difference to any client code. The question, therefore, is how we drive/specify/test that the proxy is, in fact, preventing the multiple access problem from occurring.

In part 2 we will deal with this issue.

...to be continued...


[1] Endo-Testing will be covered in a future blog
[2] If you are not familiar with the Proxy Pattern, pay a visit here: http://www.netobjectives.com/PatternRepository/index.php?title=TheProxyPattern