Search code examples
javasingletonpowermocksuppress

suppress a singleton constructor in java with powermock


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.


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());
            }
    
        }