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.

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:
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:
    @Test
    public void logTest() {
        setUpLogHandler();
        ClassUnderTest ct = new ClassUnderTest();
        ct.methodThatLogs();
        assertThat(customAppender.getMessages(), hasItem("new log message"));
    }

Thursday, April 21, 2016

Git - Rewriting Commit

Git provides an option to modify the last commit. For staging follow the normal method:

git add .
After staging the files, use the --amend option to staged files to the last commit:
git commit --amend
If no commit message is provided with -m option the previous commit message will be prompted by default. On the other hand to amend a commit without changing its commit message use the --no-edit option.

Wednesday, April 20, 2016

Parameterized JUnit Tests

JUnitParams provides a runner - JUnitParamsRunner, to add parameterized tests in a test class. With this runner parameterized and non-parameterized test methods can be combined in the same class.

@RunWith(JUnitParamsRunner.class)
public class ParameterizedTests {
    @Parameters({"1, true", "2, false"})
    @Test
    public void test1(int num, boolean result) {
        assertThat(num == 1, is(result));
    }
}
To pass in objects or null values, a separate method can be setup, which then can be added to the @Parameters annotation.
@RunWith(JUnitParamsRunner.class)
public class ParameterizedTests {
    private Object[] params() {
        return $(
                $(1, true),
                $(2, false)
        );
    }

    @Parameters(method = "params")
    @Test
    public void test1(int num, boolean result) {
        assertThat(num == 1, is(result));
    }
}

Wednesday, April 13, 2016

Maven Auto Increment Version

Maven update-versions is a handy goal to increment version number of a project. This is especially useful for large multi-module maven projects.

mvn release:update-versions
The above command prompts for the new version for each module in the project. The prompts can be avoided by using the option autoVersionSubmodules. This will set each module version to be same as the parent POM.
mvn releae:update-versions -DautoVersionSubmodules=true
The goal can be run in non-interactive mode using batch-mode.
mvn --batch-mode release:update-versions
Above command will auto increment the version without prompts. A specific version can also be specified:
mvn --batch-mode release:update-versions -DdevelopmentVersion=1.1-SNAPSHOT