如何使用JMockIt对Log4j进行单元测试?

时间:2013-12-11 09:55:41

标签: java unit-testing logging log4j jmockit

使用静态方法在类中声明Logger(或任何静态字段)时:

public class Foo {
  private static final Logger LOGGER = Logger.getLogger(Foo.getClass);
}

我可以断言调用其上的方法(用于审核)的正确方法是什么?

以下将起作用,但是setField似乎是错误的方法,无法使用@Tested批注来允许自动注入。

@Mocked Logger logger
new Expectations() {
  {
    setField(unitUnderTest, logger);
  }
}

JMockIt似乎提供了一个带有 @UsingMocksAndStubs(Log4jMocks.class)的解决方案,但这不允许对它进行Expectations,因为它会导致调用getLogger()返回一个真实但无用的Logger而不是一个模拟的实例。

3 个答案:

答案 0 :(得分:6)

很简单:

@Test
public void verifyAuditing(@Cascading final Logger logging)
{
    // Call code under test which will create auditing records.

    new Verifications() {{ logging.info("expected audit info"); }};
}

使用@Cascading会导致Logger在“级联”模式下被模拟,其中每个返回引用类型的方法都会自动创建一个模拟实例。初始模拟实例logging代表所有此类实例。

答案 1 :(得分:1)

这个问题和答案描述了我希望如何完美地做到这一点,但是我在尝试实现这里接受的答案时没有发现任何快乐 - 努力使其成功但是我的配置可能在某些重要方面有所不同?所以,如果它能帮助其他任何人,我会提供这个公认的疯狂的替代方案,它确实具有实际工作的优势(Java6,JMockit 1.7,JUnit 4.8.1,SLF4J 1.5.10)。

一般策略是实现一个将委托给模拟记录器的slf4j Logger。这种组合似乎让我能够以对被测系统完全无创的方式将所有内容连接起来。在被测系统中,我有:

private static Logger log = org.slf4j.LoggerFactory.getLogger(SUT.class);

当然,日志的各种用途......

在我的测试课中..

private static final TestLogger log = new TestLogger();

@Mocked
private Logger mockLogger;

@Tested
SUT sut = new SUT();

@BeforeClass
public static void replaceLogger() {
    new MockUp<LoggerFactory>() {
        @SuppressWarnings("rawtypes")
        @Mock
        Logger getLogger(Class clazz) {
            return log;
        }
    };
}

@Before
public void setup() {
    // go ahead and prefix this with name of test class, since log is static..
    log.setLogger(mockLogger);
}

@Test
public void ensure_some_behavior_happens() {
    new NonStrictExpectations() {{
        ...
    }};

    sut.someMethodOfInterest();

    new Verifications() {{
        mockLogger.warn("expected logging"); maxTimes = 1;
    }};
}

好吧现在疯狂的部分 - 日食帮助了很多,但实施Logger仍然是一个巨大的痛苦。真的让我欣赏界面隔离原则。

public class TestLogger implements Logger {
    Logger l = null;
    public void setLogger(Logger log) { l = log; }
    public String getName() { return l != null ? l.getName() : ""; }
    public boolean isTraceEnabled() { return l != null ? l.isTraceEnabled() : false; }
    public void trace(String msg) { if (l != null) l.trace(msg); }
    public void trace(String format, Object arg) { if (l != null) l.trace(format, arg); }
    public void trace(String format, Object arg1, Object arg2) { if (l != null) l.trace(format, arg1, arg2); }
    public void trace(String format, Object[] argArray) { if (l != null) l.trace(format, argArray); }
    public void trace(String msg, Throwable t) { if (l != null) l.trace(msg, t); }
    public boolean isTraceEnabled(Marker marker) { return l != null ? l.isTraceEnabled(marker) : false; }
    public void trace(Marker marker, String msg) { if (l != null) l.trace(marker, msg); }
    public void trace(Marker marker, String format, Object arg) { if (l != null) l.trace(marker, format, arg); }
    public void trace(Marker marker, String format, Object arg1, Object arg2) { if (l != null) l.trace(marker, format, arg1, arg2); }
    public void trace(Marker marker, String format, Object[] argArray) { if (l != null) l.trace(marker, format, argArray); }
    public void trace(Marker marker, String msg, Throwable t) { if (l != null) l.trace(marker, msg, t); }
    public boolean isDebugEnabled() { return l != null ? l.isDebugEnabled() : false; }
    public void debug(String msg) { if (l != null) l.debug(msg); }
    public void debug(String format, Object arg) { if (l != null) l.debug(format, arg); }
    public void debug(String format, Object arg1, Object arg2) { if (l != null) l.debug(format, arg1, arg2); }
    public void debug(String format, Object[] argArray) { if (l != null) l.debug(format, argArray); }
    public void debug(String msg, Throwable t) { if (l != null) l.debug(msg, t); }
    public boolean isDebugEnabled(Marker marker) { return l != null ? l.isDebugEnabled(marker) : false; }
    public void debug(Marker marker, String msg) { if (l != null) l.debug(marker, msg); }
    public void debug(Marker marker, String format, Object arg) { if (l != null) l.debug(marker, format, arg); }
    public void debug(Marker marker, String format, Object arg1, Object arg2) { if (l != null) l.debug(marker, format, arg1, arg2); }
    public void debug(Marker marker, String format, Object[] argArray) { if (l != null) l.debug(marker, format, argArray); }
    public void debug(Marker marker, String msg, Throwable t) { if (l != null) l.debug(marker, msg, t); }
    public boolean isInfoEnabled() { return l != null ? l.isInfoEnabled() : false; }
    public void info(String msg) { if (l != null) l.info(msg); }
    public void info(String format, Object arg) { if (l != null) l.info(format, arg); }
    public void info(String format, Object arg1, Object arg2) { if (l != null) l.info(format, arg1, arg2); }
    public void info(String format, Object[] argArray) { if (l != null) l.info(format, argArray); }
    public void info(String msg, Throwable t) { if (l != null) l.info(msg, t); }
    public boolean isInfoEnabled(Marker marker) { return l != null ? l.isInfoEnabled(marker) : false; }
    public void info(Marker marker, String msg) { if (l != null) l.info(marker, msg); }
    public void info(Marker marker, String format, Object arg) { if (l != null) l.info(marker, format, arg); }
    public void info(Marker marker, String format, Object arg1, Object arg2) { if (l != null) l.info(marker, format, arg1, arg2); }
    public void info(Marker marker, String format, Object[] argArray) { if (l != null) l.info(marker, format, argArray); }
    public void info(Marker marker, String msg, Throwable t) { if (l != null) l.info(marker, msg, t); }
    public boolean isWarnEnabled() { return l != null ? l.isWarnEnabled() : false; }
    public void warn(String msg) { if (l != null) l.warn(msg); }
    public void warn(String format, Object arg) { if (l != null) l.warn(format, arg); }
    public void warn(String format, Object[] argArray) { if (l != null) l.warn(format, argArray); }
    public void warn(String format, Object arg1, Object arg2) { if (l != null) l.warn(format, arg1, arg2); }
    public void warn(String msg, Throwable t) { if (l != null) l.warn(msg, t); }
    public boolean isWarnEnabled(Marker marker) { return l != null ? l.isWarnEnabled(marker) : false; }
    public void warn(Marker marker, String msg) { if (l != null) l.warn(marker, msg); }
    public void warn(Marker marker, String format, Object arg) { if (l != null) l.warn(marker, format, arg); }
    public void warn(Marker marker, String format, Object arg1, Object arg2) { if (l != null) l.warn(marker, format, arg1, arg2); }
    public void warn(Marker marker, String format, Object[] argArray) { if (l != null) l.warn(marker, format, argArray); }
    public void warn(Marker marker, String msg, Throwable t) { if (l != null) l.warn(marker, msg, t); }
    public boolean isErrorEnabled() { return l != null ? l.isErrorEnabled() : false; }
    public void error(String msg) { if (l != null) l.error(msg); }
    public void error(String format, Object arg) { if (l != null) l.error(format, arg); }
    public void error(String format, Object arg1, Object arg2) { if (l != null) l.error(format, arg1, arg2); }
    public void error(String format, Object[] argArray) { if (l != null) l.error(format, argArray); }
    public void error(String msg, Throwable t) { if (l != null) l.error(msg, t); }
    public boolean isErrorEnabled(Marker marker) { return l != null ? l.isErrorEnabled(marker) : false; }
    public void error(Marker marker, String msg) { if (l != null) l.error(marker, msg); }
    public void error(Marker marker, String format, Object arg) { if (l != null) l.error(marker, format, arg); }
    public void error(Marker marker, String format, Object arg1, Object arg2) { if (l != null) l.error(marker, format, arg1, arg2); }
    public void error(Marker marker, String format, Object[] argArray) { if (l != null) l.error(marker, format, argArray); }
    public void error(Marker marker, String msg, Throwable t) { if (l != null) l.error(marker, msg, t); }
}

无论如何,我肯定会看到其他解决方案中的其中一种解决方案是否有效,但如果你应该绝望,也许可以尝试这种方法。

编辑:好的......在我将TestLogger转变为动态代理实现之后,我对这种方法感觉更好。这是怎么做的..

首先,给自己一个界面..

public interface TestLogger extends Logger {
    void setLogger(org.slf4j.Logger wrapped);
}

然后你需要一个调用处理程序..

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;

public class TestLoggerInvocationHandler implements InvocationHandler {
    private Logger wrapped;

    private TestLoggerInvocationHandler() {
        super();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("setLogger".equals(method.getName())) {
            wrapped = (Logger)args[0];
            return null;
        }
        return wrapped != null ? method.invoke(wrapped, args) : null;
    }

    public static TestLogger createTestLogger() {
        return (TestLogger) (Proxy.newProxyInstance(TestLogger.class.getClassLoader(),
                new Class[] { TestLogger.class }, new TestLoggerInvocationHandler()));
    }

}

只需在测试类中调用静态工厂方法,你应该好好去 - 毕竟不是那么疯狂!

答案 2 :(得分:0)

您可以将字段声明为非静态字段,默认情况下使用Logger.getLogger(...),然后提供一个setter来注入模拟的记录器。如果您在与原始类相同的包中使用Test,则至少可以声明setter包受保护。

据我所知,getLogger将始终返回相同的日志记录实例,因此,即使您有数百个类的实例,也没有数百个Logger。