TotT: Using Dependancy Injection to Avoid Singletons

It's hard to test code that uses singletons. Typically, the code you want to test is coupled strongly with the singleton instance. You can't control the creation of the singleton object because often it is created in a static initializer or static method. As a result, you also can't mock out the behavior of that Singleton instance.

If changing the implementation of a singleton class is not an option, but changing the client of a singleton is, a simple refactoring can make it easier to test. Let's say you had a method that uses a Server as a singleton instance:

public class Client {
  public int process(Params params) {
    Server server = Server.getInstance();
    Data data = server.retrieveData(params);
    ...
  }
}


You can refactor Client to use Dependency Injection and avoid its use of the singleton pattern altogether. You have not lost any functionality, and have also not lost the requirement that only a singleton instance of Server must exist. The only difference is that instead of getting the Server instance from the static getInstance method, Client receives it in its constructor. You have made the class easier to test!

public class Client {
  private final Server server;

  public Client(Server server) {
    this.server = server;
  }

  public int process(Params params){
    Data data = this.server.retrieveData(params);
    ...
  }
}


When testing, you can create a mock Server with whatever expected behavior you need and pass it into the Client under test:

public void testProcess() {
  Server mockServer = createMock(Server.class);
  Client c = new Client(mockServer);
  assertEquals(5, c.process(params));
}


Remember to download this episode of Testing on the Toilet and post it in your office.

Permalink | Links to this post |
The comments you read here belong only to the person who posted them. We do, however, reserve the right to remove off-topic comments.

7 comments:

Calvin Spealman said...

This doesn't cover a few downfalls to the approach.

It requires the users of the client know about where to get the singleton and it forces them to access it for every creation of the client class.

Another problem is that the singleton that this ignores the difference in when the singleton is acquired, which could have important implications that need to be addressed.

It should be included in such a suggestion that the client class take the singleton injection as an optional parameter, and access the singleton by default, possibly and specifically on each use of the singleton.

Ran Biron said...

After several failures, we (I and my team in the undisclosed company I work for) got to couple of conclusions:
1. DI frameworks (e.g. Spring) are extremely friendly for testing.
2. If you can't use a DI framework, create (a simple) one.
Now (2) requires some explanation – what we do is have one real singleton – The singleton (application context, container, god – take your pick) which acts as a very restricted access map to several contexts "hubs". Each hubs converse directly with the map, first loading it with the required objects, then extracting and typecasting as needed. Context objects have a very small footprint on construction and are only initialized on event (application init) / request (same as real singletons)
Testing is facilitated in either of two ways:
1. If you only need a small number of mock contexts, you create them and load them manually into the singleton map. Future requests will retrieve them. On tests teardown, you empty the entire map.
2. If you need a large number of mock contexts (big unit tests / integration tests), you just fire one (or more) of the real hubs and overwrite only what's needed in the map (if anything is) with mock implementations.

This approach has the merit of being very easy on production code and yet (almost) as testable as real DI.

haemi said...

To be honest, I do not see the advantage...

With the first implementation of the Client-Class, it would have been possible to do:

Client c = new Client();
client.process();

That's all... what's the drawback here?

John said...

It's hard to test code that uses singletons. << understatement of the year :P
We have had alot of luck using DI for testing, and when singletons are necessary provide an alternative getInstance method which takes in the parameters to construct the object with for testing.

David said...

For this thing I use Guice.

sm said...

網頁設計,情趣用品店,情趣用品專賣網

A片,色情A片,免費A片,成人影片,色情影片,a片免費看,情色貼圖,情色文學,情色小說,色情小說
AV,AV女優

辣妹視訊,美女視訊,視訊交友網,視訊聊天室,視訊交友,視訊美女,免費視訊,免費視訊聊天,視訊交友90739,免費視訊聊天室,成人聊天室,視訊聊天,視訊交友aooyy
哈啦聊天室,辣妺視訊,A片,色情A片,視訊,080視訊聊天室,視訊美女34c,視訊情人高雄網,視訊交友高雄網,0204貼圖區,sex520免費影片,情色貼圖,視訊ukiss

ann said...

福~
「朵
語‧,最一件事,就。好,你西...............................................................................................................................-...相互
,以讓>它使...................