java - 使用 Mockito.doAnswer(...) 重复回答处理

标签 java unit-testing logging mockito powermockito

我正在尝试测试一个静态方法,该方法捕获来自不同子调用或方法本身的异常,并且只记录异常消息。根据要求,我无法更改方法实现。在这种情况下,监视子调用不是一种选择,因为我们希望在测试中保持一致,并且由于某些执行从方法体本身抛出异常,我们最终选择检查...错误日志消息。

因此,我想模拟将从该方法调用的记录器,当记录器调用 error(...) 时添加一个与特定消息匹配的日志调用的答案。到目前为止,在我身边这么好。我所做的一个例子如下:

Mockito.doAnswer(new Answer<Void>()
{
    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String errorMessage = (String) args[0];
        assertTrue(errorMessage.contains(SPECIFIC_MESSAGE));
        return null;
    }
}).when(logger).error(Matchers.startsWith(GENERIC_PREFIX));

我们决定使用 PowerMockito 来模拟静态调用并从 LoggerFactory.getLogger(...) 返回我们模拟的记录器
Logger logger = Mockito.mock(Logger.class); 
PowerMockito.mockStatic(LoggerFactory.class);
Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);

现在我们的问题来了:

对于每个测试,要检查的消息 SPECIFIC_MESSAGE 的值会发生变化。因此,我们为每个测试模拟一个记录器,模拟静态 LoggerFactory 调用以返回记录器,然后将我们的特定答案添加到此特定实例的 logger.error(...) 调用中。但是在我们第一次测试期间添加到 logger.error 调用中的第一个答案似乎覆盖了所有其他答案 .每次测试都会执行相同的操作。

我认为问题可能来自 Mockito,它会返回一个单例模拟,但我对此进行了测试,但事实并非如此。然后我认为我的问题可能来自我的 LoggerFactory.getLogger(...) 调用,它会一直返回相同的记录器实例,但也不是这种情况。这是一个实现,它显示了我在代码中遇到的相同问题以及相关的执行日志跟踪。
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

    public static void main(String[] args)
    {
        logger.error("Error = " + Arrays.toString(args));
    }
}

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
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.Mockito.*;
import static org.junit.Assert.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest({LoggerFactory.class})
public class AppTest
{
    @Test
    public void firstTest()
    {
        Logger logger = Mockito.mock(Logger.class); 
        PowerMockito.mockStatic(LoggerFactory.class);
        Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);

        Mockito.doAnswer(new Answer<Void>()
        {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                String errorMessage = (String) args[0];
                System.out.println("Calling from firstTest");
                assertTrue(errorMessage.contains("a"));
                return null;
            }
        }).when(logger).error(Matchers.startsWith("Error"));

        App.main(new String[]{ "a" });
    }

    @Test
    public void secondTest()
    {
        Logger logger = Mockito.mock(Logger.class); 
        PowerMockito.mockStatic(LoggerFactory.class);
        Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);

        Mockito.doAnswer(new Answer<Void>()
        {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                String errorMessage = (String) args[0];
                System.out.println("Calling from secondTest");
                assertTrue(errorMessage.contains("b"));
                return null;
            }
        }).when(logger).error(Matchers.startsWith("Error"));

        App.main(new String[]{ "b" });
    }
}

痕迹:
Calling from firstTest
Calling from firstTest

知道为什么没有处理第二个答案吗?提前致谢。

编辑:

问题实际上来自静态方法模拟。
“然后我认为我的问题可能来自我的 LoggerFactory.getLogger(...) 调用,它会一直返回相同的记录器实例,但也不是这种情况。”实际上是错误的。它在我的测试中有效,其中调用 getLogger(...) 返回在测试中模拟的记录器模拟,但是当实际调用 App.main(...) 方法时,在 main 中调用模拟的 getLogger(...) 方法(...) 始终返回相同的实例,并且不会在测试之间按预期重置。
Mockito 应该在每个文档的测试之间重置。 mockito 的错误或限制?与 Mockito 和 PowerMockito 之间的交互相关的问题?

最佳答案

我找到了一个解决方案,该解决方案是在每个测试的方法级别为类准备静态模拟。
添加 @PrepareForTest(LoggerFactory.class)在签名之前实际上解决了记录器工厂模拟在测试之间没有被重置的问题,因此第一次调用 Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);始终有效并且始终返回相同的记录器。

关于java - 使用 Mockito.doAnswer(...) 重复回答处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47015528/

相关文章:

logging - Python 日志记录 : display only information from debug level

java - 如何防止 Tomcat6 在 WEB-INF/lib 中预加载 JDBC 驱动程序?

java - 为什么JInternalFrame的setSize不起作用

android - 同时测试多个构建变体

c# - 协助对测试进行分类

python - 有没有类似 Python 的 *testability-explorer* 工具?

java - 如何读取 .logs 文档中的最后一行?

java - 多个应用程序实例日志记录

java - 获取供应商流的最小值和最大值

java - 尝试使用 maven 从命令行运行 Java7 Hello World 项目