Mockito.doAnswer(...) 的重复答案处理
Duplicate Answer processing with Mockito.doAnswer(...)
我正在尝试测试一个静态方法,该方法捕获来自不同子调用或方法本身的异常,并且只记录异常消息。根据要求,我无法更改方法实现。在这种情况下,监视子调用不是一个选项,因为我们希望在测试中保持一致,并且由于某些执行从方法主体本身抛出异常,我们最终选择检查...错误日志消息。
因此,我想模拟将从该方法调用的记录器,当记录器调用 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 来模拟静态调用,并 return 从 LoggerFactory.getLogger(...)
编辑我们的模拟记录器
Logger logger = Mockito.mock(Logger.class);
PowerMockito.mockStatic(LoggerFactory.class);
Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
现在我们的问题来了:
对于每个测试,要检查的消息 SPECIFIC_MESSAGE 的值都会发生变化。因此,我们为每个测试模拟一个记录器,模拟对记录器 return 的静态 LoggerFactory 调用,然后将我们的特定答案添加到我们对该特定实例的 logger.error(...) 调用。但是 在我们的第一次测试中添加到 logger.error 调用的第一个答案似乎覆盖了所有其他答案。每次测试都会执行相同的操作。
我认为问题可能来自 Mockito,它会 return 一个单例模拟,但我对此进行了测试,但事实并非如此。然后我认为我的问题可能来自我的 LoggerFactory.getLogger(...) 调用,它会一直 return 同一个记录器实例,但也不是这种情况。这是一个实现,它显示了我的代码与相关执行日志跟踪所遇到的相同问题。
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
知道为什么第二个答案没有得到处理吗?提前致谢。
编辑:
问题实际上来自静态方法模拟。
"Then I thought my problem could come from my LoggerFactory.getLogger(...) call which would return all the time the same instance of a logger but not the case either." 其实是错误的。它在我的测试中有效,其中调用 getLogger(...) returns 记录器模拟在测试中被模拟但是当实际调用 App.main(...) 方法时,模拟的 getLogger(. ..) main(...) 中的方法调用总是 return 相同的实例,并且不会按预期在测试之间重置。
Mockito 应该在每个文档的测试之间重置。 mockito 的错误或限制? Mockito 和 PowerMockito 交互的问题?
我找到了一个解决方案,即在每个测试的方法级别准备 class 静态模拟。
在签名之前添加 @PrepareForTest(LoggerFactory.class)
实际上解决了记录器工厂模拟在测试之间没有被重置的问题,因此第一个调用 Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
始终有效并且始终返回相同的记录器。
我正在尝试测试一个静态方法,该方法捕获来自不同子调用或方法本身的异常,并且只记录异常消息。根据要求,我无法更改方法实现。在这种情况下,监视子调用不是一个选项,因为我们希望在测试中保持一致,并且由于某些执行从方法主体本身抛出异常,我们最终选择检查...错误日志消息。
因此,我想模拟将从该方法调用的记录器,当记录器调用 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 来模拟静态调用,并 return 从 LoggerFactory.getLogger(...)
编辑我们的模拟记录器Logger logger = Mockito.mock(Logger.class);
PowerMockito.mockStatic(LoggerFactory.class);
Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
现在我们的问题来了:
对于每个测试,要检查的消息 SPECIFIC_MESSAGE 的值都会发生变化。因此,我们为每个测试模拟一个记录器,模拟对记录器 return 的静态 LoggerFactory 调用,然后将我们的特定答案添加到我们对该特定实例的 logger.error(...) 调用。但是 在我们的第一次测试中添加到 logger.error 调用的第一个答案似乎覆盖了所有其他答案。每次测试都会执行相同的操作。
我认为问题可能来自 Mockito,它会 return 一个单例模拟,但我对此进行了测试,但事实并非如此。然后我认为我的问题可能来自我的 LoggerFactory.getLogger(...) 调用,它会一直 return 同一个记录器实例,但也不是这种情况。这是一个实现,它显示了我的代码与相关执行日志跟踪所遇到的相同问题。
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
知道为什么第二个答案没有得到处理吗?提前致谢。
编辑:
问题实际上来自静态方法模拟。 "Then I thought my problem could come from my LoggerFactory.getLogger(...) call which would return all the time the same instance of a logger but not the case either." 其实是错误的。它在我的测试中有效,其中调用 getLogger(...) returns 记录器模拟在测试中被模拟但是当实际调用 App.main(...) 方法时,模拟的 getLogger(. ..) main(...) 中的方法调用总是 return 相同的实例,并且不会按预期在测试之间重置。 Mockito 应该在每个文档的测试之间重置。 mockito 的错误或限制? Mockito 和 PowerMockito 交互的问题?
我找到了一个解决方案,即在每个测试的方法级别准备 class 静态模拟。
在签名之前添加 @PrepareForTest(LoggerFactory.class)
实际上解决了记录器工厂模拟在测试之间没有被重置的问题,因此第一个调用 Mockito.when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
始终有效并且始终返回相同的记录器。