Search code examples
javastaticmockingpowermockpowermockito

How to undo/reset PowerMockito.mockStatic?


Assuming I have the following classes

public class StaticClass {

    public static void staticMethod() throws SomeException {
        System.out.println("staticMethod");
    }

    private StaticClass() {
    }
}

and

public class SomeClass {

    public void someMethod() {
        try {
            StaticClass.staticMethod();
        }catch(SomeException ex) {
            System.out.println("SomeException occurred");
            return;
        }
        System.out.println("SomeException didn't occur");
    }
}

which I'm testing with

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticClass.class)
public class SomeClassTest {

    @Test
    public void testStaticMethod() throws Exception {
        mockStatic(StaticClass.class);
        doThrow(new SomeException("unimportant message")).when(StaticClass.class,
                "staticMethod");
        //test something where exception is needed
        SomeClass instance = new SomeClass();
        try {
            instance.someMethod();
            fail("IllegalStateException expected");
        }catch(IllegalStateException expected) {
        }
        //now test something where exception isn't needed
        instance.someMethod();
    }
}

How can I undo the static mocking/the configuration to throw SomeException so that I can test the code after the try-catch block in the second instance.someMethod()?

PowerMock: How to unmock a method? doesn't apply because there's no mock reference to pass to Mockito.reset and passing StaticClass causes java.lang.ClassCastException: java.lang.Class cannot be cast to org.mockito.internal.creation.bytebuddy.MockAccess.

SomeException simply extends Exception.

A SSCCE is provided at https://gitlab.com/krichter/powermock-undo-statik-mocking.

I'm using PowerMock 1.7.3.


Solution

  • My opinion, but in general a unit test should exercise a single code path. (I think of this as applying single responsibility to a test method.)

    Bu my suggestion about splitting the tests does solve the problem. I don't know the details, but @PrepareForTest provides a fresh StaticClass for each test.

    These separate tests work:

    @Test
    public void testStaticMethodWhenSomethingUnexpectedHappens() throws Exception {
        mockStatic(StaticClass.class);
        // changed exception type
        doThrow(new IllegalStateException("unimportant message")).when(StaticClass.class, "staticMethod");
    
        SomeClass instance = new SomeClass();
        try {
            instance.someMethod();
            fail("IllegalStateException expected");
        } catch (IllegalStateException expected) {
        }
    
        // added verification
        verifyStaticMethodWasInvokedOneTime();
    }
    
    @Test
    public void testStaticMethodHappyPath() throws Exception {
        mockStatic(StaticClass.class);
        doNothing().when(StaticClass.class, "staticMethod");
    
        SomeClass instance = new SomeClass();
        instance.someMethod();
    
        // added verification
        verifyStaticMethodWasInvokedOneTime();
    }
    
    private void verifyStaticMethodWasInvokedOneTime() throws SomeException {
        verifyStatic(StaticClass.class);
        StaticClass.staticMethod();
    }