7

I've read most SO related questions ( here, here and there). The last question proposes four alternatives to make code which calls static methods unit-testable. I want to ask about my particular case: We have a "business logic layer" or "rules" project which contains 45 static classes (no state, just static methods). Moreover, they are not easily testable by themselves: most of them access the database and file system. It's not that bad, anyway: to access the database, they use the unique instance of some Mapper class (all Mappers are Singletons). Whenever I try to unit test something, I run into this wall. The biggest problem is that this is very, very important code, and changes to it should be planned very carefully. My question: How should I go about making this more unit testable? Should I write 45 interfaces and use dependency injection? Even so, how do I stub/mock Mappers?

PS: I've been reading Michael Feathers' "Working with Legacy Code", so direct references are welcome (other books too :)

Edit: Since some people said solutions might be platform-dependent, I'm working on .NET (C# and some VB.NET)

Community
  • 1
  • 1
dario_ramos
  • 7,118
  • 9
  • 61
  • 108

5 Answers5

10

The current situation is probably that no one dares to change anything in the code because it might break in unexpected ways. Make sure that everyone understands that you are improving the situation: Your changes might break the code but unlike before, those breakages will be found and once they have been found, they will be fixed forever.

That said, the next step depends on your experience and your credit with the team. If you want to play safe, use code like this (Java syntax):

Mapper {
    public static Mapper INSTANCE = new Mapper(); // NEW code

    protected void doImpl() { // NEW CODE
        ... code copied from impl()... // OLD code, NEW PLACE
    }

    public static void impl() { // OLD code
        INSTANCE.doImpl(); // NEW code
    }

    // OLD code ...
}

This is a very simple change which allows you to overwrite INSTANCE from your tests. For production code, you don't do anything and the default will make the code behave exactly like before.

That way, you can replace one method at a time. You can stop following this path at any time - each change takes only a couple of minutes and it's a refactoring: The code does exactly what it did before. Since each change is so small and can't break anything, you can replace one method, write all the unit tests that you couldn't write before, rinse, repeat. Lastly, if you don't want/need to rework all static methods, this approach gives you all the leeway you could ask for.

In a second step, you can introduce DI or any other technology which will make you happy. The advantage of this approach: When you come to the complex changes, you will already have unit tests that'll protect you.

If you started with DI, you'd have to change a lot of code in all kinds of places - without proper unit tests that could protect you.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
3

Make the interface of the Mapper class and replace the mapper functionality with a new moc implementation. I think that you will need to implement the abstract factory which will generate the instances of Mappers. So make an IMapperFactory and two implementations of this DBMapperFactory and MocMapperFactory pass the instance of this object to your code where you access the Mappers and generate these with using this instance.

Gl.

AlexTheo
  • 4,004
  • 1
  • 21
  • 35
  • 1
    Sounds ok. That would take care of making the static methods testable, but how about the code which calls them? On another note, just curious... Is "GI" some sort of signature, or is it an acronym? (I'm from Argentina) – dario_ramos Nov 08 '11 at 13:29
  • Gl == Good Luck :). For the code which uses the Mapper you have to adapt it to use the factory interface instead of getInstance by singleton. – AlexTheo Nov 08 '11 at 15:31
2

Your question is really platform related, in the Microsoft world you can use Microsoft Moles to test static methods. And mock static methods.

In the java world there are probably other tools or you should avoid using statics.

On other platforms there are other tools etc.

In general, any static method will make your code less testable. In my option it is better to avoid statics and singletons (global state) at any cost. Testable code is the most important part of your code if you what to change anything later on. Testable code is normally also more readable.

Moles and Pex

Peter
  • 27,590
  • 8
  • 64
  • 84
1

Static class can be mocked, although without knowing what language/environment you're using, there's no way to say how.

I had a codebase that ended up exactly like what you said; we created interfaces and default implementations of a few dozen static classes (don't panic--code generation). The default impls just delegated to the singletons.

The calling classes were switched to DI using the default impls, but were then much easier to test. The static classes with dependencies on other static classes or singletons moved to using the default impls.

You mock a singleton like any other class--if it has a getInstance (or equivalent) you can make it return whatever you want. Or you can go the same route and use DI.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
1

Excellent question! You can mock your Mapper class by creating a new one that inherits from the singleton Mapper class then and use it to intercept all calls to your database.

GETah
  • 20,922
  • 7
  • 61
  • 103