Re: [powermock] Using PowerMock for mocking static properties with mockito @InjectMocks

1,224 views
Skip to first unread message

Johan Haleby

unread,
Nov 4, 2012, 1:10:47 AM11/4/12
to powe...@googlegroups.com
Hi,

Sorry for the late response, I've been on vacation. You're right that InjectMocks is executed before the setup methods so that's expected behavior. I would also encourage you to have a look at spring mvc test or rest assured to test http controllers/resources.

Regards,
/Johan

On Wed, Oct 24, 2012 at 5:43 PM, Amir Zucker <the...@gmail.com> wrote:

I'm using PowerMock and Mockito to test a Spring controller.

I have the class TestController defined (see below, Snippet #1), and a Unit Test defined for it (see below, Snippet #2). But when I try to unit test it I get an exception (see below, Snippet #3).

If I remove @InjectMocks, remove the TestController instantiation on definition, and do controllerUT = new TestController() in the test function, it works fine (see below, Snippet #4).

This leads me to believe that the static replacement does not happen before @InjectMocks, and my question is if this is the way things work, or am I doing something wrong? Is there a better way to design the code to avoid this issue? I'm guessing people use static log assignment (I did not invent this) so someone must have run into this issue before...

Thanks!

Snippet #1

@Controller
@RequestMapping("/api/test")
public class TestController {
    private static final Logger LOG = LoggerFactory.getLogger(TestController.class);

    @Autowired
    private GeneralService generalService;

    @RequestMapping(method=RequestMethod.GET)
    public void doSomethingUseful(
        HttpServletRequest request,
        HttpServletResponse response) {
    // nothing userful to do right now
    }
}

Snippet #2

@RunWith(PowerMockRunner.class)
@PrepareForTest({TestController.class, LoggerFactory.class})
public class TestControllerTest {
    @InjectMocks
    private TestController controllerUT = new TestController();

    @Mock
    private GeneralService service;

    @Mock
    private Logger loggerMock;

    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;

    @Before
    public void setUp() {
        PowerMockito.mockStatic(LoggerFactory.class);
        when(LoggerFactory.getLogger(any(Class.class))).
            thenReturn(loggerMock);
    }

    @Test
    public void doSomethingUsefulTest() {
        controllerUT.doSomethingUseful(request, response);
        assert(true);
    }
}

Snippet #3

Failed to auto configure default logger context
Reported exception:
ch.qos.logback.core.joran.spi.JoranException: Parser configuration error occurred
    at ch.qos.logback.core.joran.event.SaxEventRecorder.buildSaxParser(SaxEventRecorder.java:86)
    at ch.qos.logback.core.joran.event.SaxEventRecorder.recordEvents(SaxEventRecorder.java:57)
    at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:132)
    at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:96)
    at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:55)
    at ch.qos.logback.classic.util.ContextInitializer.configureByResource(ContextInitializer.java:75)
    at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:148)
    at org.slf4j.impl.StaticLoggerBinder.init(StaticLoggerBinder.java:84)
    at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:54)
    at org.slf4j.LoggerFactory.bind(LoggerFactory.java:128)
    at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:108)
    at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:279)
    at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:252)
    at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:265)
    at com.basicservice.controller.TestController.<clinit>(TestController.java:39)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at javassist.runtime.Desc.getClassObject(Desc.java:43)
    at javassist.runtime.Desc.getClassType(Desc.java:152)
    at javassist.runtime.Desc.getType(Desc.java:122)
    at javassist.runtime.Desc.getType(Desc.java:78)
    at com.basicservice.controller.TestControllerTest.<init>(TestControllerTest.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.createTestInstance(PowerMockJUnit44RunnerDelegateImpl.java:188)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.createTest(PowerMockJUnit44RunnerDelegateImpl.java:173)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:195)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassCastException: org.apache.xerces.jaxp.SAXParserFactoryImpl cannot be cast to javax.xml.parsers.SAXParserFactory
    at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:128)
    at ch.qos.logback.core.joran.event.SaxEventRecorder.buildSaxParser(SaxEventRecorder.java:79)
    ... 42 more

Snippet #4

@RunWith(PowerMockRunner.class)
@PrepareForTest({TestController.class, LoggerFactory.class})
public class TestControllerTest {
    private TestController controllerUT;

    @Mock
    private GeneralService service;

    @Mock
    private Logger loggerMock;

    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;

    @Before
    public void setUp() {
        PowerMockito.mockStatic(LoggerFactory.class);
        when(LoggerFactory.getLogger(any(Class.class))).
            thenReturn(loggerMock);
    }

    @Test
    public void doSomethingUsefulTest() {
        controllerUT = new TestController();
        controllerUT.doSomethingUseful(request, response);
        assert(true);
    }
}


--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To view this discussion on the web visit https://groups.google.com/d/msg/powermock/-/KedSroRaWDUJ.
To post to this group, send email to powe...@googlegroups.com.
To unsubscribe from this group, send email to powermock+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/powermock?hl=en.

Amir Zucker

unread,
Nov 4, 2012, 5:50:58 AM11/4/12
to powe...@googlegroups.com
Hi,

Thanks for your response, hope you enjoyed your vacation :)
One question though, how would you solve this issue?  The example is a controller, but this is relevant to any class, not just controllers.  How do I use InjectMocks when I have a static member which needs to be properly initialized by a static library with a mock upon class creation?

I'm pretty sure I'm not the only one using a Logger this way, so this must have been resolved somehow (or is the answer simply not to use InjectMocks?)

Thanks,
Amir

Johan Haleby

unread,
Nov 4, 2012, 8:42:30 AM11/4/12
to powe...@googlegroups.com
Thanks :) 

Well first of all you can't inject mocks to static fields if I remember it correctly and you definitely can't inject mocks to static final fields. What you probably want to do in this case is actually not mocking but stubbing or suppressing. Or are you looking for how to capture/validate the log output? The solution is different depending on what you want to achieve. If you just want to get rid of the actual logger you can use a mock policy and you can also suppress or stub the call to log instance without replacing the actual log instance using the stubbing API:

// Simply suppress all calls to "log.info"
suppress(method(Logger.class, "info"));

// Capture method calls to log.info with an invocation handler
replace(method(Logger.class, "info").with(new InvocationHandler() {.. });

// Replace "info" method with another method that you've created yourself
replace(method(Logger.class, "info").with(method(MyClass.class, "myFakeInfoMethod"));
To view this discussion on the web visit https://groups.google.com/d/msg/powermock/-/qZLQDv2A2BoJ.

Amir Zucker

unread,
Nov 4, 2012, 8:59:15 AM11/4/12
to powe...@googlegroups.com
MockPolicy looks like an exact answer to my issue, and actually takes care of the exact situation with slf4j.   I knew someone already hit this wall :)
Thanks mate!

Johan Haleby

unread,
Nov 4, 2012, 9:10:29 AM11/4/12
to powe...@googlegroups.com
Glad you got it working :)

To view this discussion on the web visit https://groups.google.com/d/msg/powermock/-/uMuQEEgMLKsJ.
Reply all
Reply to author
Forward
0 new messages