Androidsx Androidsx | android and wearable developers

 

Subscribe for market insights and new posts

 

Can’t test that Singleton? Try Dependency Injection!

November 11, 2009 at 9:40 pm | blog, software quality | 10 comments

 

So you want to test this method:

public class Client {

    public int process(Params params) {
        final Server server = Server.getInstance();
        final Data data = server.retrieveDate(params);
        // do stuff
    }
}

We don’t want to retrieve an instance of a real server for our little unit-test, so how can we test this method?

It is hard to test code that uses singletons.

We don’t control the creation of the singleton object, as it is performed inside a static method. There is no way to mock the object in order to test the behavior of our method in isolation.

Refactor it to use Dependency Injection.

You can refactor Client to avoid using the singleton pattern. Instead of obtaining the Server instance from the static getInstance() method, allow Client to accept it through its constructor.

public class Client {
    private final Server server;  

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

    public int process(Params params) {
        final Data data = server.retrieveData(params);
        // do stuff
    }
}

Let’s write that test now:

@Test
public void testConnectionUpTime() {
    final Server mockServer = Mockito.mock(Server.class);
    final Params params = // ...
    Mockito.when(mockServer.process(params)).thenReturn(5);
    final Client client = new Client(mockServer);
    assertEquals(5, client.process(params));
}

The code is now both clearer and testable.

The dependency between the client and the server is now explicit: Client client = new Client(server);. There is no way a developer creates a client instance without noticing that a server instance must be configured: it is a parameter in the constructor.

The singleton allowed to create a client instance without configuring the server in advance. The object would be successfully created and the application would execute, until one of the methods runs into a non-configured/non-reachable/null server and fail at runtime :’-(

Related posts

Tags: , ,

<< Back to Blog Discuss this post

 

Checkout our new pet project:

Subscribe to our newsletter

 

10 Comments to “Can’t test that Singleton? Try Dependency Injection!”

  1. david says:

    Dependency injection to mock singleton ?
    A Very bypassing solution.
    If the singleton is central and you need the singleton in 20 classes of your application, you will add the dependency in the 20 classes ?
    You can but you make your design heavier.

  2. But most likely those 20 classes only need access to specific objects/services that the server provides. So you would inject only what they really need (following the Law of Demeter). This way, they are not even aware of the server class, which might come in handy in a future refactoring.

    Still, sometimes there are trade-offs to make. What can easily happen with dependency injection is that your constructors end up having too many arguments. Another problem is that some classes are just forwarding objects that they don’t even need to others.

    To alleviate this, you can “start the dependency injection later”: let a selected group of central classes get the server instance, and inject from there.

    Not sure I made my point. It’s sort of difficult to explain without an example and with my limited literary skills :)

  3. Bhavinkumar says:

    hi,

    I am learning mockito and unittesting.

    I am writing testcase for a Class , which Uses singleton class instance inside one method. The method that gives the instance is “Static”.

    for ex:

    public class abc{

    public init(){
    XYZConfiguration xyz = XYZConfiguration.getInstance();
    }

    }

    // This class is initialized with SPRING
    // Singleton Class
    public class XYZConfiguration {

    private static XYZConfiguration = null;

    public static getInstance() {

    if(XYZConfiguration ==null)
    throw new exception();
    else
    return XYZConfiguration;
    }

    }

    can you suggest me any idea, how to use mock and spy on this ??

    Thanks,
    Bhavin

  4. @Bhavinkumar
    The example you write is exactly what we are trying to avoid, by applying dependency injection. In its current state, you would have a hard time testing your class abc, since you can’t use a fake object or a mock instead of XYZConfiguration.

    Let me first rewrite your example into something that compiles fine:
    {code starts}
    class Abc {
    public void init() {
    XyzConfiguration xyz = XyzConfiguration.getInstance();
    xyz.configureXyz();
    }

    public void someMethod() {
    System.out.println(“Some method works fine”);
    }
    }

    /** Singleton that holds the configuration for XYZ */
    class XyzConfiguration {
    private static XyzConfiguration XYZ_CONFIGURATION = null;

    /** This class is not instantiable from outside. Use {@link #getInstance}. */
    private XyzConfiguration() {
    }

    public static XyzConfiguration getInstance() {
    if (XYZ_CONFIGURATION == null) {
    XYZ_CONFIGURATION = new XyzConfiguration();
    }
    return XYZ_CONFIGURATION;
    }

    public void configureXyz() {
    System.out.println(“Configure XYZ”);
    }
    }

    public class AbcTest {
    Abc abc;

    @Before
    public void setUp() {
    abc = new Abc();
    abc.init();
    }

    @Test
    public void testSomeMethod() {
    abc.someMethod();
    // Assert something, to make sure it worked…
    }
    }
    {code ends}

    Now, if the configuration implies using a production system, writing into a database and whatnot, this test will be slow and dangerous.

    To fix this, let’s apply dependency injection, and this is the final result:

    {code begins}
    class Abc {
    private final XyzConfiguration xyz;

    public Abc(XyzConfiguration xyz) {
    this.xyz = xyz;
    }

    public void init() {
    xyz.configureXyz();
    }

    public void someMethod() {
    System.out.println(“Some method works fine”);
    }
    }

    interface XyzConfiguration {
    public void configureXyz();
    }

    /** Singleton that holds the configuration for XYZ */
    class DefaultXyzConfiguration implements XyzConfiguration {
    private static XyzConfiguration XYZ_CONFIGURATION = null;

    /** This class is not instantiable from outside. Use {@link #getInstance}. */
    private DefaultXyzConfiguration() {
    }

    public static XyzConfiguration getInstance() {
    if (XYZ_CONFIGURATION == null) {
    XYZ_CONFIGURATION = new DefaultXyzConfiguration();
    }
    return XYZ_CONFIGURATION;
    }

    @Override
    public void configureXyz() {
    System.out.println(“Configure XYZ”);
    }
    }

    public class AbcTest {
    Abc abc;

    @Before
    public void setUp() {
    abc = new Abc(new FakeXyzConfiguration());
    abc.init();
    }

    @Test
    public void testSomeMethod() {
    abc.someMethod();
    // Assert something, to make sure it worked…
    }

    class FakeXyzConfiguration implements XyzConfiguration {
    @Override
    public void configureXyz() {
    System.out.println(“Lightweight configuration, just for testing”);
    }
    }
    }
    {code ends}

    Now you can test Abc in isolation. And, as a bonus, now your Abc class doesn’t even notice about changes in the implementation of XYZ configuration. Now it is only aware of the interface.

    Hope it helps, and sorry that the code is not properly formatted!

  5. wmadjboespjety, Priligy kostar, VVzpmiy, [url=http://priligyuk.com/]Priligy[/url], JYyOOTC, http://priligyuk.com/ Priligy t, pEpzury.

  6. fsdudboespjety, Intranasal ativan, OlFKLFN, [url=http://www.watertherapists.com/]What is ativan injection use for in the er[/url], nJpBoFa, http://www.watertherapists.com/ Ativan addiction, nzjsWGe.

  7. xujmtboespjety, Buy fioricet codeine, zhnzYaj, [url=http://www.greatestgrains.com/?page_id=5]Online pharmacy fioricet[/url], mbpMjxZ, http://www.greatestgrains.com/?page_id=5 Fioricet and, mFelLRy.

  8. Cialis says:

    tzpmgboespjety, Buy cialis, iqHbtvI, [url=http://preemptivelove.org/]Male enhancements viagra and cialis[/url], joJjvXj, http://preemptivelove.org/ Price of cialis, yFyhYfI.

  9. private charter plane private jets for hire corporate jet charter private jet charter service rental private jets private jet for hire

  10. It is 1 of guarantee, of hope, of energy, of feelings and of practicality, as it is a journey of spirituality.
    Mercury will be passing by a star these days, a power that blends intellect with integrity.

Leave a Comment

Categories:

Recent posts:

Search:

Subscribe