Last Friday could be my happiest day in 2015 because I've figured out how could I override
log4j logger in unit test. Take the use case below as an example, I'll have the
logger being declared as private static member:
import org.apache.log4j.Logger;
public class Helper {
private static Logger jdbcLogger = Logger.getLogger("jdbcLogger");
...
...
}
This was a nightmare because
JDBC logger is very high depends on the database's availability, especially when I've been told to freeze any update into the database. End up I create a local database just for this purpose, by doing so, I'm required to reconfigure the JDBC connection in my source code. This still isn't a clean solution as I don't want
Jenkins to reconfigure the database configuration in the raw code. Then I start to think about the hijacking way, this is what I did to hijack the logger:
private void hijackJdbcLogger() {
Logger l = Whitebox.getInternalState(Helper.class, "jdbcLogger");
l.removeAllAppenders();
l.addAppender(appender);
Whitebox.setInternalState(Helper.class, "jdbcLogger", l);
}
But later I found out this solution has a flaw where
jdbcLogger must first initialize by
Helper class before I can change its value. At least I have something surprise me where I mock the whole logger:
@BeforeClass
public static void setUpBeforeClass() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Logger mockLogger = Logger.getLogger("unitTestLogger");
mockLogger.setLevel(Level.DEBUG);
mockLogger.setAdditivity(false);
mockLogger.addAppender(EasyMock.createMock(ConsoleAppender.class));
Field logger = ReportHelper.class.getDeclaredField("jdbcLogger");
logger.setAccessible(true);
logger.set(null, mockLogger);
}
Now I feel the perfection with this solution design. Again, in rule of design, hijacking is very rude, don't do this. When something has declared as private, meaning it is not suppose to be exposed to the outside world. Code review is indeed a higher priority in such case.
No comments:
Post a Comment