Issues in breaking dependencies for unit testing

One of the most important issues I've come to realize is the various ways of dealing with legacy code, mainly - how to deal with breaking multiple dependencies on an existing class or method that needs to be tested.

There are two basic ways to break dependencies, with various "dialects"  for each one. The both have their pros and cons and I won't explain in detail how to do them, but mainly, which of them you should choose and when.

 

Mock Objects

The first method of breaking dependencies is that of "Mock Objects".

In short, it relates to how one would send an instance of a "fake" class to be interacted with from the class under test instead of the *real* class. For example. to test a method that calls various Logging methods against a class that logs stuff to the database, you might be inclined to replace the instance of the logging class with that of a fake one, so that any calls made to the log will not actually go to a database, and thus will run much faster and won't need any configurations before the tests are run. That fake class can also tell us whether the "Log" method was called or not and how many times, since it is totally under our tester's control (they created it).

This article will deal mainly with the second main approach to testability, so if you’d like to read more about the Mock Objects method, there is plenty of material available on this over at www.MockObjects.com (also called the “Dependency Inversion” or “Inversion of Control” pattern). Martin Fowler also writes about this pattern in this fine article, though it is less related to testing and more generic in nature.

 

Extract & Override

The second method of breaking dependencies is much closer to the tested code, and relies heavily on inheritance.

It is usually called "Extract & Override".

The main idea here is that instead of "faking" the objects which are problematic. you "fake" the actual calls to them. An example might be in order.

Suppose we have a class called "MyCalculator" which contains code like this:

 

public double Calculate(int x, int y)

{

   if(x>1000)

   {

   Logger.StaticLog("Calc":Got an illegal number");//problematic call

   throw new Exception("Illegal number");

   }

}

 

suppose we want to test this method.  And suppose Logger.StaticLog() takes 10 seconds to do its magic (you'd probably do that asynchronous but this example is good enough). Obviously any tests that run this method will take too long to run, and unfortunately, this is a static method, so we can't easily "inject" or "replace" the Logger with a fake instance since there is no instance to work with. So instead, we will refactor our code to make it testable like this:

 

public double Calculate(int x, int y)

{

   if(x>1000)

   {

   callLog("Calc":Got an illegal number");//refactored

   throw new Exception("Illegal number");

   }

}

 

protected virtual void callLog(string text)

{

   Logger.StaticLog(text);

}

 

the idea is that in our tests, we won't really use this original calc class, but instead inherit from it and override the "callLog" method to not really do anything (which means you'll want to make those overridable methods with as little logic as possible) like this

 

[Test]

[ExpectedException(typeof(Exception))]

public void Calculate_IllegalNumber_ThrowsException()

{

   TestableCalc c = new TestableCalc();

   c.Calculate(1001, 0);

}

 

class TestableCalc: Calculator

{

public string callLogText = string.Empty;

   protected override void callLog(string text)

   {

   callLogtext = text;

   }

}

Notice that the second method allows you to break even very hard dependencies in a relatively easy manner.

 

Pros and Cons of Mock Objects

Pros

-         Most mock object frameworks allow you test test interaction between objects in a very easy manner, by allowing you to add any number of “expectations” and return values to your mock objects that will be tested against. The syntax is clear and all of the hard work of maintaining the state of the cals to the mock object is performed with the mock object system.

-         The last sentence means that tests involving a single mock object will be very readable and easy to write, especially when multiple calls are expected against it, or when parameter values are being tests for method calls (for example, to make sure that a Logger class is called 3 times with different values each time would be very easy to write with a framework such as NMock or NUnit.Mocks)

 

Cons

-         the more depdencies you need to break on the tested object (legacy systems are usually just the thing that might have that) the mock Mock objects and stub objects you’ll need to create and initialize in your tests, and the more refactoring you will have to do to the system to make it testable (adding new interfaces, constructors, parameters and such..). this may prove almost impossible on some systems where you don’t own all the code and where refactoring needs to be done on code that you don’t own.

-         The more dependencies means also less readable tests because all the initialization is not readable at all when its 20 lines of code or more before the actual test takes place. Even if you refactor your mock initialization code it might still not be very readable.

-         Mock objects don’t help when you try to deal with code that calls static methods, singletons and other types of calls in which an object instance cannot be replaced (there are ways to refactor singletons into testability but you might not have that luxury)

 

 

 

Pros & Cons od Extract & Override

Pros

-         It’s much faster and easier to refactor the system into testability with Extract& Override (perhaps this should be named “Extractoring” ? ) You don’t need to introduce new interfaces into a system and the only requirements are that the class is overridable and that the methods are overridable (protected)

-         It’s faster and easier to write tests for such a system because test setup and object initialization is easier and clearer.

o       To mimic system behavior override a method and return what you want using a publicly exposed member on the inheritor class or throw an exception by checking a public flag

o       To check interaction calls simply check a publicly exposed member (“wasMethodXXCalled” or “GetMethodXXPassedValues()” on the inheritor class

-         You can break any dependency you like right at the source.

o       You can override a Boolean validation check such as if(configurationSettings[“key”]==value) into if(hasKeyValue(value))

o       You can override full calls for static methods such as turning LogClass.StaticLog(“text”) into callLog(“text”)

Cons

-         You have to do all the grunt work of verifying calls to methods, including parameter values, yourself. When we used mock objects, the mock object framework we used did that for us automatically. But when we do this manually  (i.e inherit our own class and override methods) we need to make the code work on our own which can lead to long code inside the overriding class.

-         If you need to override some methods and not others in a dynamic way (i.e different tests override different methods) you need to either implement base-forwariding logic into the overridden methods or create multiple inheriting classes which override different methods. In either case, it can get out of hand quickly.

-         In some types of applications, especially Real-Time, where performance matters a lot, adding many virtual method calls may actually be a hindrance. There are ways around that though, using various conditional-compilation techniques which are not discussed in this article.

 

Hidden bugs with E&O – biggest hazard of all

-         When you override a method call, you usually end up with at least (hopefully only) one line of code in the actually virtual base method which simply calls some external dependency and may return a value. There is a problem here that may not be recognizable at first. To explain the problem I’ll need a little example.

 

 

Here’s the code from the refactored class in the first example:

 

public double Calculate(int x, int y)

{

   if(x>1000)

   {

   callLog("Calc":Got an illegal number");//refactored

   throw new Exception("Illegal number");

   }

}

 

protected virtual void callLog(string text)

{

   Logger.StaticLog(text);

}

 

Now, consider what happens if suddenly, the design changes a bit and the logger takes in another parameter (and designs change with time). The new signature for the Logger.StaticLog now looks like this:

 

Public static void StaticLog(string text)

Public static void StaticLog(int severity, string text)

 

 

There is a new parameter for severity of type int, and this is a new overload for the previous method. Fine and dandy. Now suppose we want to change our code to send a severity each time we call the log instead of using the old method. We might refactor the virtual method to take another parameter such as :

 

 

protected virtual void callLog(int severity,string text)

{

   Logger.StaticLog(text);

}

 

But notice that we don’t actually change the actual call to the static log method. In fact, in our test we would do the same thing as in the first example – override the virtual method and make sure it is called with the correct parameters. So our tests might look like this:

 

[Test]

public void Calculate_IllegalNumber_LogsSeverity()

{

   TestableCalc c = new TestableCalc();

   c.Calculate(1, 0);

   Assert.AreEqual(100, c.callLogSeverity);

   Assert.AreEqual(“illegal number”, c.callLogText);

 

}

 

class TestableCalc: Calculator

{

public string callLogText = string.Empty;

public int callLogSeverity = 0;

 

   protected override void callLog(int severity,string text)

   {

   callLogtext = text;

callLogSeverity = severity

   }

}

 

We’re overridng the callLog method to make sure it is called with the correct values.

To make this test pass our code might look like this:

//the method we are testing

public double Calculate(int x, int y)

{

   if(x>1000)

   {

   callLog(100,"illegal number");

   throw new Exception("Illegal number");

   }

}

 

protected virtual void callLog(int severity,string text)

{

//notice that there is a bug here!!

   Logger.StaticLog(text);

}

 

Yes – this example will actually pass the callLog test, even though there is an obvious bug in it – we don’t really call the logger correctly!

When you think about it, who’s to say that inside the real virtual method the code actually calls the correct static method? No one. We could make our code call the “callLog” refactored method correctly, but we have no way to assure that the actual line inside the CallLog method actually calls the method correctly. That’s because we actually override that call in our tests. That’s a dangerous place to be and you need to watch out for making mistakes on this. Your interaction test might say that everything is ok, but you may forget to actually call the real methods correctly in the virtual calls.

This type of hazard does not exist with mock objects since with mock objects we don’t override any real implementation – just interfaces and method signatures.

This problem may not be as big as you think – I’ve rarely come across it. It’s kinda hard to find though, and it requires debugging to dig out. It would have been cought earlier with real Mock objects instead of using Mock Methods

 

 

Summary

Hopefully, you’ve seen that all good things have a dark side, but that doesn’t mean we can’t use them, we just need to know where the dark side begins and make sure we don’t go there (or that we at least know the way back). Mock objects and E&O(“close refactoring” as I like to call it)  are two great routes to a testable application.

You’ll find the need to use E&O much more in legacy code than you would in new, green-field code, where you get the chance to write it testable from the beginning (Test-Driven code is often a great example of using interfaces and Mock Objects with the possibility of never needing to E&O anything).

 

The end result is all about being able to produce something out of these tests, and you’ll need to figure out which of these approaches works best for you.

I’d also recommend Michael Feathers’ great book entitled  Working Effectively with legacy code” for more methods and refactoring patterns to break code dependencies. Indeed, everything I’ve outlined here also appears there and may be considered an extension to some of the books ideas and thoughts.

 

You can also check out some of the Unit Testing Guidelines I keep adding to over time – there’s a whole category of them in my weblog.

 

Published Wednesday, August 24, 2005 1:33 AM by RoyOsherove
Filed under:

Comments

Tuesday, August 23, 2005 8:36 PM by Jay Flowers

# re: Issues in breaking dependencies for unit testing

I was just about to blog some myself on what principles one should follow to make code testable. The most important one I can think of is Inversion of Dependancy.

But when that has not been followed and there are no abstractions allowing for static mocks or the test subject creates its dependancies directly baring dynamic mocks things get tricky. I have been waiting for a dynamic AOP framework to help. It just so happens that Spring.NET 1.0 just came out and it includes AOP support.

http://www.springframework.net/doc/reference/html/aop.html

"Around advice: Advice that surrounds a joinpoint such as a method invocation. This is the most powerful kind of advice. Around advice will perform custom behaviour before and after the method invocation. They are responsible for choosing whether to proceed to the joinpoint or to shortcut executing by returning their own return value or throwing an exception."

I believe this kind of technology will go a long way to making traditionally untestable code testable. One should be able to isolate an object/memeber to test its responsibilities with out the rest of the system having to be brought into the mix.

This in no way should become an excuse to write crappy code (I.E. I know how to refactor so I can write crappy code and refactor it latter).
Wednesday, August 24, 2005 6:19 AM by Jim Arnold

# re: Issues in breaking dependencies for unit testing

Thursday, August 25, 2005 9:18 AM by Ian Ringrose

# “Extract” is a good first step in both cases.

Doing a “Extract & Override” means that at a later stage it is easier to reflector the code to use a none static logger as there is only one place to change in the class. It also give a good place to set a breakpoint on when debugging the code, how nice to be able to break into the debugger just after the error has happened, but before the exception is thrown.
Thursday, August 25, 2005 1:36 PM by Eli Lopian

# re: Issues in breaking dependencies for unit testing

Roy,
I don't understand why you are still trying to find difficult ways to break dependancies when there is a FREE tool out that can do the job.
We have over 20 developers using TypeMock.NET (http://www.typemock.com) that does all the dirty work for us.
It save a load of time compared to changing your code trying to make it testable - around 20% compared to the techniques that you discussed.

I agree with Jay that this is not an excuse to write crappy code.
Although it is not an opensource project is still comes with a Free community edition, and no, I am not assosiated with the company.

Thursday, August 25, 2005 1:54 PM by Roy Osherove

# re: Issues in breaking dependencies for unit testing

Eli:
Yes, TypeMock is kinda cool in the easy with which you are able to break things up, but it still can cause trouble. Even with TypeMock.NET - your tests setups might still look horribly unreadable and long if you have many dependencies to break. Sometime just because you have a tool means you have an execuse to write not only slopy code but sloppy tests as well (breaking 10 dependencies before each test is very hard to read and understand rather than seeing two-three lines of code stating that some method is overriden.
Second of all - not all languages have the benefit of using TypeMock.NET - they *have* to resort to more manual means of doing things.
Wednesday, September 07, 2005 2:44 AM by Steve Freeman

# re: Issues in breaking dependencies for unit testing

The thing to remember about Mock Objects is that it's an extension of Test-First Programming: it makes most sense when you use it to drive your design. Just adopting Mocks without adjusting your coding style leads to the drawbacks that everyone likes to point out. If you follow the tests, you end up with smaller, focussed objects communicating through interfaces. Done consistently, this leads to a system that's easier to maintain and to reuse.

The best explanation we have at the moment is our OOPLSA paper at: http://www.jmock.org/oopsla2004.pdf
Monday, October 19, 2009 8:54 AM by Extract and Conquer! « Relentless Development

# Extract and Conquer! « Relentless Development

Pingback from  Extract and Conquer! « Relentless Development