Question

I'm trying to unit-test some classes that make use of a Singleton class whose constructor does some things I can't (and shouldn't) do from the unit-test environment. My ideal scenario would be to end up with the constructor completely suppressed and then stub out the other member methods that my test classes invoke. My problem is that I can't seem to get the constructor suppressed.

My understanding of a way to solve this would be something like the following:

public class MySingleton extends AbstractSingletonParent {
    public final static MySingleton Only = new MySingleton();
    private MySingleton(){
        super(someVar); // I want the super-class constructor to not be called
        //
        //more code I want to avoid
    }

    public Object stubbedMethod() {}
}

public class ClassToBeTested {
    public void SomeMethod(){
        Object o = MySingleton.Only.stubbedMethod();
    }
}


@RunWith(PowerMockRunner.class)
@PrepareForTest(MySingleton.class)
public class TestClass {
    @Test
    public void SomeTest() {
        suppress(constructor(MySingleton.class));
        mockStatic(MySingleton.class);

        PowerMock.replay(MySingleton.class);
        // invoke ClassToBeTested, etc

        PowerMock.verify(MySingleton.class);

        //make some assertions
    }
}

Unfortunately during the createMock invocation, the MySingleton constructor is hit, and it still calls the super constructor.

Am I doing something silly? I found an example on the web doing almost exactly this, but it was using a deprecated suppressConstructor method. Despite the deprecation I tried that, too, to no avail...

Is what I'm trying to do possible? If so, what am I doing wrong?

*Edited version now works.

Was it helpful?

Solution

You need to annotate TestClass with the @PrepareForTest annotation so it has a chance to manipulate the bytecode of the singletons.

Also, the superclass ctor supression signature should include somevar's class; right now you're just suppressing the default ctor.

See the @PrepareForTest API docs. Here's a blog post with some more details as well.

FWIW, it's working for me:

@RunWith(PowerMockRunner.class)
@PrepareForTest({EvilBase.class, NicerSingleton.class})
public class TestEvil {

    @Test
    public void testEvil() {
        suppress(constructor(EvilBase.class));
        assertEquals(69, EvilBase.getInstance().theMethod());
    }

    @Test public void testNice() {
        suppress(constructor(EvilBase.class));
        suppress(constructor(NicerSingleton.class));
        assertEquals(42, NicerSingleton.getInstance().theMethod());
    }

}

OTHER TIPS

How about you set the instance field ('only' in your code) of your Singleton with an instance instantiated with the constructor you want (you can do all of this with the Reflection API or dp4j).

The motivating example of a dp4j publication discusses that.

I am not sure what is it that you are doing wrong. But on the design side, i can suggest you look into dependency injection i.e. DI.

For making your code testable, make use of DI. With DI you would pass the singleton class as an constructor argument to your test class. And now since you pass an argument, inside your test case you can create a custom implementation of the AbstractSingleton class and your test case should work fine.

With DI, your code will become more testable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top