Dennis Burton's Develop Using .NET

Change is optional. Survival is not required.
Tags:

Speaking at CodeStock 2009

This year CodeStock is shaping up to be a conference like no other. Most regional conferences have around 25 sessions with some attempt to do Open Spaces on the side. CodeStock 2009 will certainly blow the doors off the norm with a community driven event with something to offer for everyone. To start with, it is the first conference that I know of that allowed the early registering attendees to choose the direction for the more than 50 hour-long sessions as well as 6 extended hands-on sessions. Then, to kick it up one more notch, Alan Stevens will be facilitating the Open Spaces. Make no mistake: Alan knows how to do Open Spaces right. Last year’s CodeStock was the moment the community took notice of the way that Alan’s particular talent set meshes with coordinating an exceptionally effective Open Space event. This is truly an event that has something to offer everyone.

Given the community selection process, it is even more of an honor to be selected to speak at this event. I will be giving the PatternsInTesting presentation that has already been well received in the Midwest area.

Indy Code Camp 2009

I was privileged to be able to give the PatternsInTesting presentation at Indy Code Camp this year. There were many really good testing related conversations before and after the presentation. I am encouraged by the amount of interest the community is taking towards improving the adoption of test driven design. I also got to see a few top notch presentations:

  • Tim Wingfield - Care About Your Craft: A very motivating presentation on doing the right thing
  • Philip Japikse - CRUD Sucks! NHibernate to the rescue: Phil has an impressive in depth knowledge of NHibernate. I was most fascinated by all of the extensions that he has written to really make NHibernate hum.
  • Jon Fuller - Dealing with Dependencies: This was my favorite presentation of the day. Jon had only enough slides for an overview and went directly in to writing code. It was an in depth, working look at using DI tools.
  • Michael Eaton - Developing Solid WPF Applications: A very informative view into a real world WPF application and its development evolution

Greater Lansing .net User Group

A big thank you to GLUGnet for letting me do the PatternsInTesting presentation for the group. This is a group that I frequently attend, it is nice to be able to present on a topic that I talk about so much with the members of that group.

Tags: patterns | programming | testing

Your mission, should you choose to accept it, is to observe the interaction with an object and verify that this interaction is in your best interest.

The Scenario

Some objects that you have to consume are just poorly written. The ones that are most egregious always seem to be the ones you have no control over. That lack of control may be because you have no access to the source, it may be because it would be a political minefield to change the source, or a lack of tests makes the team afraid to change the source. It seems like every time I go to a new client, these objects exist (as well as the political minefields). The developers have a mysterious set of incantations that they have memorized for interaction with these objects in order to avoid bugs. Often, no one knows where these "rules" came from and they are usually not written down.

The Vocabulary

The name of this pattern is the Spy. What the spy will do is capture information about interaction with an object, and only take action if the need arises. A spy object looks just like the object that you need to interact with (i.e. implements the same public interface) such that your code should not even notice it is even there, but, behind the scenes it will perform validation and give the useful feedback you wish the original object would have implemented in the first place. This spy or validation wrapper is commonly implemented by holding on to a reference to original object. This allows for the spy to call the actual implementation in order to preserve the original behavior.

The ideal world

In an ideal world, the kinds of interactions that you are trying to validate with a spy should be captured be captured by the actual object you are interacting with, rather than the spy. Having a wrapper object whose function is validation is a massive code smell. If you have the ability to fix the original code by adding the relevant validation, that is by far a better solution than creating a spy object.

The real world

In reality, you do not have access to change the source of third party libraries, even if sometimes that third party is a couple of buildings or even cubes away. The first thing you should do when you run in to these bizarre incantations “required” for successful object interaction, is to ask “why?” and be persistent, dig deep. You may be (not so) surprised that most of the reasons have long since gone away. If you do find that some of the hidden rules are indeed valid, you need a way to validate that your code is following the rules.

Example

Consider the following example where MethodToObserve will throw an uninformitive exception if the PropertyToObserve has not yet been set.

    1 public interface IInterfaceToUse

    2 {

    3     void MethodToObserve();

    4     List<string> PropertyToObserve { get; set; }

    5 }

    6 

    7 public class ClassToUse : IInterfaceToUse

    8 {

    9     public void MethodToObserve()

   10     {

   11         PropertyToObserve.ForEach(str =>

   12                  Console.WriteLine("Calling the MethodToObserve:" + str) );

   13     }

   14 

   15     private List<string> propertyToObserve;

   16     public List<string> PropertyToObserve

   17     {

   18         get

   19         {

   20             Console.WriteLine( "Calling the PropertyToObserve setter");

   21             return propertyToObserve;

   22         }

   23         set

   24         {

   25             Console.WriteLine("Calling the PropertyToObserve getter");

   26             propertyToObserve = value;

   27         }

   28     }

   29 }

As mentioned previously, the ideal solution is to fix the implementation. If your only access to this code is Reflector or you are just not authorized to change it, the next best thing is to protect yourself (flaming email to the author of the code is,of course, optional). Our protection, or at least better information will come from a class implementing IInterfaceToUse just like the original, only this time the implementation will provide the consumer with information that they can act on.

    1 public class ValidatingObserver : IInterfaceToUse

    2 {

    3     private IInterfaceToUse _observedClass;

    4 

    5     public ValidatingObserver(IInterfaceToUse observedClass)

    6     { _observedClass = observedClass; }

    7 

    8     public void MethodToObserve()

    9     {

   10         if (PropertyToObserve == null)

   11             throw new ArgumentNullException("PropertyToObserve",

   12                                             "Property must be set prior to calling Method");

   13         // perform observations

   14         Console.WriteLine("The spy is watching: MethodToObserve");

   15 

   16         // pass through to implementing object

   17         _observedClass.MethodToObserve();

   18     }

   19 

   20     public List<string> PropertyToObserve

   21     {

   22         get

   23         {

   24             Console.WriteLine("The spy is watching: PropertyToObserve getter");

   25             return _observedClass.PropertyToObserve;

   26         }

   27         set

   28         {

   29             Console.WriteLine("The spy is watching: PropertyToObserve setter");

   30             _observedClass.PropertyToObserve = value;

   31         }

   32     }

   33 }

Note that this time, instead of the “oops, I forgot something exception” known in .net as the “Object reference not set to instance of an object” exception, we get meaningful information about what is missing and even some hint as to how to fix it. The error now clearly states that the PropertyToObserve should be set prior to calling the MethodToObserve.

Using the spy

There are many ways to create a spy object, I chose containment for this post; You may also use derivation to create your wrapper. Derivation will get you up and running faster, and you will not have maintenance work to do if you add a method to the interface, but this will come at a cost. Containment will allow you to swap out the actual implementation of the object with a mock implementation at some time in the future. As always consider you needs before choosing a spy implementation.

The test below shows how to use the spy created in this post

    1 [TestFixture]

    2 class ManualObserverTests

    3 {

    4     [Test]

    5     public void MethodCallSpy()

    6     {

    7         var observedClass = new ClassToUse();

    8         var validatingObserver = new ValidatingObserver(observedClass);

    9 

   10         Assert.Throws<ArgumentNullException>( validatingObserver.MethodToObserve);

   11     }

   12 }

Tool Support

All of the previous posts in this series have mentioned leveraging tools to assist in creating these test objects. They spy object however is a strange beast; the demands it places on the tools turn out to create as much code as the manually coded version. If you are so inclined, you can use Rhino for a spy object. What is required is taking advantage of the Do extension method. Do takes a delegate as a parameter that matches the signature of the method being called. So what you will end up establishing is an Expectation that a method will be called and when it is Do the operation specified by the delegate.

Summary

The spy object reminds me of the Broken Windows section of the Pragmatic Programmer. It clearly states not to live with broken windows, but if you cannot fix the window, at least put a board over the window. In the case of third party code where you cannot change the source code, a validation wrapper is the board you need to keep further damage from occurring and show other developers in the area that you still care about the quality of code.

The Series

PatternsInTesting[2] - Stub Pattern PatternsInTesting[4] - Mock Pattern

Dennis Burton

View Dennis Burton's profile on LinkedIn
Follow me on twitter
Rate my presentations
Google Code repository

Community Events

Windows Azure Boot Camp Lansing GiveCamp