简体   繁体   English

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

[英]How can I unit test Log4j using JMockIt?

When a Logger (Or any static field) is declared in a class using a static method: 使用静态方法在类中声明Logger(或任何静态字段)时:

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

What is the correct way in which I can assert that methods on it are called (for auditing)? 我可以断言调用它的方法(用于审计)的正确方法是什么?

The following will work , but setField appears to be the wrong way to go about it, nullifying the use of the @Tested annotation to allow automatic injection. 以下方法可行 ,但setField似乎是错误的方法,无法使用@Tested批注来允许自动注入。

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

JMockIt seems to provide a solution with @UsingMocksAndStubs(Log4jMocks.class) , however this does not allow Expectations on it as it causes calls to getLogger() to return a real, but useless, Logger rather than a mocked instance. JMockIt似乎提供了@UsingMocksAndStubs(Log4jMocks.class)的解决方案,但是这不允许Expectations,因为它导致调用getLogger()返回一个真实但无用的Logger而不是一个模拟的实例。

It's simple: 这很简单:

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

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

The use of @Cascading causes Logger to be mocked in "cascading" mode, where every method which returns a reference type automatically creates a mocked instance. 使用@Cascading会导致Logger在“级联”模式下被@Cascading ,其中每个返回引用类型的方法都会自动创建一个模拟实例。 The initial mocked instance logging represents all such instances. 初始模拟实例logging代表所有此类实例。

This question and answer describes what I hoped to do so perfectly, yet I found no joy in my attempt to implement the answer accepted here--tried hard to make it work but maybe my configuration is different in some significant way?? 这个问题和答案描述了我希望如此完美地完成的工作,但是我在尝试实现这里接受的答案时没有找到任何乐趣 - 努力使其工作但是我的配置可能在某些重要方面有所不同? So, in case it will help anyone else, I'll offer this admittedly batsh!t-crazy alternative that does have the advantage of actually working for me (Java6, JMockit 1.7, JUnit 4.8.1, SLF4J 1.5.10). 所以,如果它能帮助其他任何人,我会提供这个公认的疯狂的替代方案,它确实具有实际工作的优势(Java6,JMockit 1.7,JUnit 4.8.1,SLF4J 1.5.10)。

The general strategy was to implement a slf4j Logger which will delegate to a mock logger. 一般策略是实现一个slf4j Logger,它将委托给一个模拟记录器。 This combination seemed to allow me to get everything wired up right in a way that was totally non-invasive to the system under test. 这种组合似乎让我能够以对被测系统完全无创的方式将所有内容连接起来。 In the system under test, I have: 在被测系统中,我有:

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

And, of course, various usages of log.. 而且,当然,日志的各种用法..

In my test 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;
    }};
}

Ok the crazy part now--eclipse helped quite a bit, but still a major pain to implement Logger. 好吧现在疯狂的部分 - 日食帮助了很多,但仍然是实施Logger的一个主要痛苦。 Really made me appreciate the Interface Segregation Principle. 真的让我欣赏界面隔离原则。

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); }
}

Anyway, I would definitely see if the one of the other solutions works first, but if you should get desperate, maybe give this approach a try. 无论如何,我肯定会看到其他解决方案中的其中一个是否有效,但如果你应该绝望,也许可以尝试这种方法。

EDIT: Ok..I'm feeling altogether better about this approach after I turned the TestLogger above into a dynamic proxy implementation. 编辑:好的......在我将TestLogger转变为动态代理实现之后,我对这种方法感觉更好。 Here's how to go with that.. 这是怎么做的..

First, give yourself an interface.. 首先,给自己一个界面..

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

Then you'll need an invocation handler.. 然后你需要一个调用处理程序..

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()));
    }

}

Just call the static factory method in the test class and you should be good to go--not so crazy after all! 只需在测试类中调用静态工厂方法,你应该好好去 - 毕竟不是那么疯狂!

You could declare the field non-static, with Logger.getLogger(...) as default, and then provide a setter to inject the mocked logger. 您可以将字段声明为非静态字段,默认情况下使用Logger.getLogger(...),然后提供一个setter来注入模拟的记录器。 If you have the Test in the same package as the original class, you can at least declare the setter package-protected. 如果您在与原始类相同的包中使用Test,则至少可以声明setter包受保护。

As far as I know, getLogger would always return the same logging instance, so even if you have hundreds of instances of your class, you don't have hundreds of Loggers around. 据我所知,getLogger将始终返回相同的日志记录实例,因此即使您有数百个类的实例,也没有数百个Logger。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM