0

I have a java class - very typical of the usual singleton - like this :

PLEASE NOTE : I have left out the "if null" logic here, for the sake of brevity, because that isn't what I am having trouble with, and I don't want to crowd the question.

public class MySingleton
{
    ObjectMapper mapper;

    private MySingleton()
    {
        new MySingleton(new ObjectMapper())
    }

    private MySingleton(ObjectMapper mapper)
    {
        this.mapper = mapper;
    }

    private static final class Lazy
    {
        static final MySingleton INSTANCE = new MySingleton();
    }

    public static MySingleton getInstance()
    {
        return Lazy.INSTANCE;
    }
}

Now - that is great - and that works - but what if I am trying to test this in a unit test...

I want to mock the mapper - so I can do :

ObjectMapper mockObjectMapper = mock(ObjectMapper.class)

But, then when I need to somehow call the constructor of "MySingleton" in order to test it...

How do I do that - given that from my test class, I know it will say "MySingleton(arguments here) has private access in MySingleton"?

MickeyThreeSheds
  • 986
  • 4
  • 23
  • 42
  • 2
    Exactly what requirements are you trying to test? I don't see any logic in any of these methods that it would make sense to test. Usually, trying to test one-line methods is a complete waste of time. – Dawood ibn Kareem Sep 29 '17 at 22:49
  • "I have left out the "if null" logic here" If you're using the lazy holder idiom, you shouldn't need any "if null" logic. – Andy Turner Sep 29 '17 at 22:51
  • Look at PowerMock framework. – tsolakp Sep 29 '17 at 23:01
  • @AndyTurner - you are indeed correct - I can pull that code out completely - but do you have any advice for my question? I am kind of baffled at how I might begin testing... – MickeyThreeSheds Sep 29 '17 at 23:03
  • @tsolakp - I am unsure about how that would get past my "static" problem? – MickeyThreeSheds Sep 29 '17 at 23:04
  • @DawoodibnKareem - I mean, the question does say "Like this" - the question is written in a way so at to not overcomplicate stackoverflow with unnecessary code - I just need to know how to create my instance from a test class. – MickeyThreeSheds Sep 29 '17 at 23:05
  • @MickeyThreeSheds PowerMock will allow you to mock `new ObjectMapper` call and allow you pass the `mockObjectMapper` instead. – tsolakp Sep 29 '17 at 23:07
  • Well you've designed this class so that the only way to get an instance of it is with the `getInstance` method, so that's what you'll have to use. – Dawood ibn Kareem Sep 29 '17 at 23:08
  • @DawoodibnKareem - cool - but is there a way to adapt this type of code to also be able to take arguments when creating the instance? – MickeyThreeSheds Sep 29 '17 at 23:10
  • You could provide another implementation of `getInstance` but you'd start to lose the point of having a singleton. – Dawood ibn Kareem Sep 29 '17 at 23:12
  • What about using @VisibleForTesting on the constructor that can take arguments? I really only need the arguments for the sake of unit testing is the thing... – MickeyThreeSheds Sep 30 '17 at 00:03
  • Perhaps the best solution for problems like this is to use a dependency injection framework like Dagger 2 or Guice since they can manage Singletons for you without you – David Rawson Sep 30 '17 at 06:13

2 Answers2

0

Singletons are the enemies of testable code. The links given in the answer to that question are an excellent argumentation on what's evil with singletons as well as why and how to avoid them.

Frank Neblung
  • 3,047
  • 17
  • 34
0

You can use PowerMock to inject your test ObjectMapper instance using ConstructorMocking.

http://benkiefer.com/blog/2013/04/23/powermockito-constructor-mocking/

I had to modify your example singleton so that the constructors were chained correctly.

public class MySingleton {
  ObjectMapper mapper;

  private MySingleton()
   {
      //This does not work.
      //new MySingleton(new ObjectMapper());

      this(new ObjectMapper());
   }

  private MySingleton(ObjectMapper mapper)
   {
     this.mapper = mapper;
   }

  private static final class Lazy
   {
     static final MySingleton INSTANCE = new MySingleton();
   }

  public static MySingleton getInstance()
   {
      return Lazy.INSTANCE;
   }
}

I also stubbed the ObjectMapper Class.

public class ObjectMapper {
    //Empty Sample uses default CTR
}

I was able to test this as follows using the instructions from the link previously listed:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class) 
@PrepareForTest(MySingleton.class) 
public class MySingletonTest {


    @Test
    public void testSingletonCtr() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        PowerMockito.whenNew(ObjectMapper.class).withNoArguments().thenReturn(mapper);

        Assert.assertEquals(MySingleton.getInstance().mapper, mapper);
    } 
}

I am doing this in a maven project. I needed the following dependencies added to my test scope:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4-rule</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>

I do tend to agree that Singletons tend to cause problems long-term for code maintenance and scalability. If you have capacity to look for alternative approaches to your problem it may benefit you to do so. If not, then I believe that the PowerMock utility will provide you the capability you're looking for.

Best of Luck.

Jeremiah
  • 1,145
  • 6
  • 8