使用 PowerMock 和 Mockito 模拟 Logger 和 LoggerFactory [英] Mocking Logger and LoggerFactory with PowerMock and Mockito

查看:68
本文介绍了使用 PowerMock 和 Mockito 模拟 Logger 和 LoggerFactory的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想模拟以下 Logger,但为了验证是否调用了日志条目,而不是为了内容.

I have the following Logger I want to mock out, but to validate log entries are getting called, not for the content.

private static Logger logger = 
        LoggerFactory.getLogger(GoodbyeController.class);

我想模拟用于 LoggerFactory.getLogger() 的任何类,但我不知道如何做到这一点.这就是我到目前为止的结果:

I want to Mock ANY class that is used for LoggerFactory.getLogger() but I could not find out how to do that. This is what I ended up with so far:

@Before
public void performBeforeEachTest() {
    PowerMockito.mockStatic(LoggerFactory.class);
    when(LoggerFactory.getLogger(GoodbyeController.class)).
        thenReturn(loggerMock);

    when(loggerMock.isDebugEnabled()).thenReturn(true);
    doNothing().when(loggerMock).error(any(String.class));

    ...
}

我想知道:

  1. 我可以模拟静态 LoggerFactory.getLogger() 以适用于任何类吗?
  2. 我似乎只能在 @Before 中运行 when(loggerMock.isDebugEnabled()).thenReturn(true); ,因此我似乎无法更改特征每个方法.有没有办法解决这个问题?
  1. Can I Mock the static LoggerFactory.getLogger() to work for any class?
  2. I can only seem to run when(loggerMock.isDebugEnabled()).thenReturn(true); in the @Before and thus I cannot seem to change the characteristics per method. Is there a way around this?

<小时>

编辑发现:

我以为我已经试过了,但没有用:

I thought I tried this already and it didnt work:

 when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);

但是谢谢你,因为它确实有效.

But thank you, as it did work.

但是我尝试了无数变体来:

However I have tried countless variations to:

when(loggerMock.isDebugEnabled()).thenReturn(true);

我无法让 loggerMock 在 @Before 之外改变它的行为,但这只会发生在 Coburtura 上.使用 Clover 时,覆盖率显示为 100%,但仍然存在问题.

I cannot get the loggerMock to change its behavior outside of @Before but this only happens with Coburtura. With Clover, the coverage shows 100% but there is still an issue either way.

我有一个简单的类:

public ExampleService{
    private static final Logger logger =
            LoggerFactory.getLogger(ExampleService.class);

    public String getMessage() {        
    if(logger.isDebugEnabled()){
        logger.debug("isDebugEnabled");
        logger.debug("isDebugEnabled");
    }
    return "Hello world!";
    }
    ...
}

然后我有这个测试:

@RunWith(PowerMockRunner.class)
@PrepareForTest({LoggerFactory.class})
public class ExampleServiceTests {

    @Mock
    private Logger loggerMock;
    private ExampleServiceservice = new ExampleService();

    @Before
    public void performBeforeEachTest() {
        PowerMockito.mockStatic(LoggerFactory.class);
        when(LoggerFactory.getLogger(any(Class.class))).
            thenReturn(loggerMock);

        //PowerMockito.verifyStatic(); // fails
    }

    @Test
    public void testIsDebugEnabled_True() throws Exception {
        when(loggerMock.isDebugEnabled()).thenReturn(true);
        doNothing().when(loggerMock).debug(any(String.class));

        assertThat(service.getMessage(), is("Hello null: 0"));
        //verify(loggerMock, atLeast(1)).isDebugEnabled(); // fails
    }

    @Test
    public void testIsDebugEnabled_False() throws Exception {
        when(loggerMock.isDebugEnabled()).thenReturn(false);
        doNothing().when(loggerMock).debug(any(String.class));

        assertThat(service.getMessage(), is("Hello null: 0"));
        //verify(loggerMock, atLeast(1)).isDebugEnabled(); // fails
    }
}

在三叶草中,我展示了 if(logger.isDebugEnabled()){ 块的 100% 覆盖率.但是,如果我尝试验证 loggerMock:

In clover I show 100% coverage of the if(logger.isDebugEnabled()){ block. But if I try to verify the loggerMock:

verify(loggerMock, atLeast(1)).isDebugEnabled();

我得到零交互.我也试过 PowerMockito.verifyStatic();在 @Before 中,但也有零交互.

I get zero interactions. I also tried PowerMockito.verifyStatic(); in @Before but that also has zero interactions.

这看起来很奇怪,Cobertura 显示 if(logger.isDebugEnabled()){ 不是 100% 完成,而 Clover 确实如此,但两者都同意验证失败.

This just seems strange that Cobertura shows the if(logger.isDebugEnabled()){ as being not 100% complete, and Clover does, but both agree the verification fails.

推荐答案

EDIT 2020-09-21:自 3.4.0 起,Mockito 支持模拟静态方法,API 仍在孵化中并且可能会发生变化,尤其是在 stubbing 和确认.它需要 mockito-inline 工件.而且您不需要准备测试或使用任何特定的跑步者.您需要做的就是:

EDIT 2020-09-21: Since 3.4.0, Mockito supports mocking static methods, API is still incubating and is likely to change, in particular around stubbing and verification. It requires the mockito-inline artifact. And you don't need to prepare the test or use any specific runner. All you need to do is :

@Test
public void name() {
    try (MockedStatic<LoggerFactory> integerMock = mockStatic(LoggerFactory.class)) {
        final Logger logger = mock(Logger.class);
        integerMock.when(() -> LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
        new Controller().log();
        verify(logger).warn(any());
    }
}

此代码中的两个重要方面是您需要在静态模拟应用时确定范围,即在此 try 块内.并且您需要从 MockedStatic 对象调用存根和验证 api.

The two inportant aspect in this code, is that you need to scope when the static mock applies, i.e. within this try block. And you need to call the stubbing and verification api from the MockedStatic object.

@Mick,也尝试准备静态字段的所有者,例如:

@Mick, try to prepare the owner of the static field too, eg :

@PrepareForTest({GoodbyeController.class, LoggerFactory.class})

我只是制作了一个小例子.首先是控制器:

EDIT1 : I just crafted a small example. First the controller :

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Controller {
    Logger logger = LoggerFactory.getLogger(Controller.class);

    public void log() { logger.warn("yup"); }
}

然后测试:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Controller.class, LoggerFactory.class})
public class ControllerTest {

    @Test
    public void name() throws Exception {
        mockStatic(LoggerFactory.class);
        Logger logger = mock(Logger.class);
        when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
        
        new Controller().log();
        
        verify(logger).warn(anyString());
    }
}

注意进口!类路径中值得注意的库:Mockito、PowerMock、JUnit、logback-core、logback-clasic、slf4j

Note the imports ! Noteworthy libs in the classpath : Mockito, PowerMock, JUnit, logback-core, logback-clasic, slf4j

因为这似乎是一个流行的问题,我想指出这些日志消息是否那么重要并且需要进行测试,即它们是功能/业务的一部分系统然后引入一个真正的依赖来明确这些日志是特征,这在整个系统设计中会好得多,而不是依赖于记录器的标准和技术类的静态代码.

EDIT2 : As it seems to be a popular question, I'd like to point out that if these log messages are that important and require to be tested, i.e. they are feature / business part of the system then introducing a real dependency that make clear theses logs are features would be a so much better in the whole system design, instead of relying on static code of a standard and technical classes of a logger.

对于这个问题,我建议使用诸如 reportIncorrectUseOfYAndZForActionXreportProgressStartedForActionX 之类的方法制作类似 = Reporter 的类.这将有利于使阅读代码的任何人都可以看到该功能.但它也将有助于实现测试,更改此特定功能的实现细节.

For this matter I would recommend to craft something like= a Reporter class with methods such as reportIncorrectUseOfYAndZForActionX or reportProgressStartedForActionX. This would have the benefit of making the feature visible for anyone reading the code. But it will also help to achieve tests, change the implementations details of this particular feature.

因此您不需要像 PowerMock 这样的静态模拟工具.我认为静态代码可以,但是一旦测试需要验证或模拟静态行为,就必须重构并引入明确的依赖项.

Hence you wouldn't need static mocking tools like PowerMock. In my opinion static code can be fine, but as soon as the test demands to verify or to mock static behavior it is necessary to refactor and introduce clear dependencies.

这篇关于使用 PowerMock 和 Mockito 模拟 Logger 和 LoggerFactory的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆