Saturday, April 23, 2016

Unit Testing Log Statements

Sometimes asserting on a log statement might be the only way of validating a method call. One way of asserting log statements is to create a custom appender and attaching it to the logger to capture the logs. log4j-api provides the framework to create a custom appender.

  1. import org.apache.logging.log4j.core.Filter;  
  2. import org.apache.logging.log4j.core.Layout;  
  3. import org.apache.logging.log4j.core.LogEvent;  
  4. import org.apache.logging.log4j.core.appender.AbstractAppender;  
  5.   
  6. import java.io.Serializable;  
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9.   
  10. public class CustomAppender extends AbstractAppender {  
  11.   
  12.     private List<string> messages = new ArrayList<>();  
  13.   
  14.     public CustomAppender(String name, Filter filter, Layout<!-- extends Serializable--> layout) {  
  15.         super(name, filter, layout);  
  16.     }  
  17.   
  18.     public CustomAppender(String name, Filter filter, Layout<!-- extends Serializable--> layout, boolean ignoreExceptions) {  
  19.         super(name, filter, layout, ignoreExceptions);  
  20.     }  
  21.   
  22.     @Override  
  23.     public void append(LogEvent event) {  
  24.         byte[] data = getLayout().toByteArray(event);  
  25.         messages.add(new String(data).trim()); // optional trim  
  26.     }  
  27.   
  28.     @Override  
  29.     public void stop() {  
  30.           
  31.     }  
  32.   
  33.     public List<string> getMessages() {  
  34.         return messages;  
  35.     }  
  36.   
  37. }</string></string>  
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class CustomAppender extends AbstractAppender {

    private List messages = new ArrayList<>();

    public CustomAppender(String name, Filter filter, Layout layout) {
        super(name, filter, layout);
    }

    public CustomAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions) {
        super(name, filter, layout, ignoreExceptions);
    }

    @Override
    public void append(LogEvent event) {
        byte[] data = getLayout().toByteArray(event);
        messages.add(new String(data).trim()); // optional trim
    }

    @Override
    public void stop() {
        
    }

    public List getMessages() {
        return messages;
    }

}
Following method adds the custom appender to the logger:
  1. private void setUpLogHandler() {  
  2.         final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);  
  3.         final AbstractConfiguration config = (AbstractConfiguration) ctx.getConfiguration();  
  4.   
  5.         // Create and add the appender  
  6.         customAppender = new CustomAppender("Custom"null, PatternLayout.createDefaultLayout());  
  7.         customAppender.start();  
  8.         config.addAppender(customAppender);  
  9.   
  10.         // Create and add the logger  
  11.         AppenderRef[] refs = new AppenderRef[]{AppenderRef.createAppenderRef("Custom"nullnull)};  
  12.         LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.INFO, ClassUnderTest.class.getCanonicalName(), "true", refs, null, config, null);  
  13.         loggerConfig.addAppender(customAppender, nullnull);  
  14.         config.addLogger(ClassUnderTest.class.getCanonicalName(), loggerConfig);  
  15.         ctx.updateLoggers();  
  16.     }  
private void setUpLogHandler() {
        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        final AbstractConfiguration config = (AbstractConfiguration) ctx.getConfiguration();

        // Create and add the appender
        customAppender = new CustomAppender("Custom", null, PatternLayout.createDefaultLayout());
        customAppender.start();
        config.addAppender(customAppender);

        // Create and add the logger
        AppenderRef[] refs = new AppenderRef[]{AppenderRef.createAppenderRef("Custom", null, null)};
        LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.INFO, ClassUnderTest.class.getCanonicalName(), "true", refs, null, config, null);
        loggerConfig.addAppender(customAppender, null, null);
        config.addLogger(ClassUnderTest.class.getCanonicalName(), loggerConfig);
        ctx.updateLoggers();
    }
Below is an example test method which asserts on a method that logs:
  1. @Test  
  2. public void logTest() {  
  3.     setUpLogHandler();  
  4.     ClassUnderTest ct = new ClassUnderTest();  
  5.     ct.methodThatLogs();  
  6.     assertThat(customAppender.getMessages(), hasItem("new log message"));  
  7. }  
    @Test
    public void logTest() {
        setUpLogHandler();
        ClassUnderTest ct = new ClassUnderTest();
        ct.methodThatLogs();
        assertThat(customAppender.getMessages(), hasItem("new log message"));
    }

No comments: