Mocking org.apache.log4j.Logger? Powermock? How can I?

I am wondering what is the easiest and most concise way to mock org.apache.log4j with powermock (and mockito).

I tried several approaches (which I will not illustrate here), but have not yet found a way to achieve what I want. I created a simple class for testing below, and I want to call the run method and verify that the log.info message was called. How can I do it? I am sure it is very easy when you know how to do it!

(I use @Rule as I want to work in the spring test, but that doesn't matter).

Thanks for the million for the correct code.

import org.apache.log4j.Logger; import org.junit.Rule; import org.junit.Test; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; public class MockLog4JTest { @Rule public PowerMockRule rule = new PowerMockRule(); private static class ClassUnderTest { private static Logger logger = Logger.getLogger(ClassUnderTest.class); public void run() { logger.info("Hello."); } } @Test public void testLog() { ClassUnderTest classUnderTest = new ClassUnderTest(); classUnderTest.run(); } } 
+4
source share
2 answers

Chris and Fieldor are correct to try to mock Logger.getLogger (). Unfortunately, the way Log4J works makes it technically difficult.

Here is the code that I came up with (tested) based on your example above.

ClassUnderTest.java

 import org.apache.log4j.Logger; public class ClassUnderTest { private static Logger logger = Logger.getLogger(ClassUnderTest.class); public void run() { logger.info("Hello."); } } 

MockLog4JTest.java

 import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.mock; import org.apache.log4j.Logger; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import org.powermock.reflect.Whitebox; @PrepareForTest(ClassUnderTest.class) public class MockLog4JTest { @Rule public PowerMockRule rule = new PowerMockRule(); @BeforeClass public static void oneTimeSetup() { System.setProperty("log4j.defaultInitOverride", Boolean.toString(true)); System.setProperty("log4j.ignoreTCL", Boolean.toString(true)); } @Test public void testLog() { ClassUnderTest classUnderTest = new ClassUnderTest(); Logger mockLogger = mock(Logger.class); Whitebox.setInternalState(ClassUnderTest.class, "logger", mockLogger); classUnderTest.run(); verify(mockLogger).info(eq("Hello.")); } } 

I decided to go using Whitebox to set the static field in the tested class to my mockLogger instance. After that, the check was pretty simple.

+10
source

Matt Lachman's answer worked fine for me - until I tried it with Spring. In Spring, I got a run-time exception when trying to change the logger to mockLogger. To make it work in Spring, I had to do the following:

change the line

 Whitebox.setInternalState(ClassUnderTest.class, "logger", mockLogger); 

to

 EncapsulationBreaker.setFinalStatic(ClassUnderTest.class.getDeclaredField("logger"), mockLogger); 

and EncapsulationBreaker looks like this:

  public class EncapsulationBreaker { public static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } } 

For more information about setting up personal static finite elements, see Change a personal static final field using Java reflection.

Also note: I am only doing this for testing.

+1
source

All Articles