我正在寻找一种方法来验证Mockito,在测试期间没有与给定的模拟进行任何交互。对于具有验证模式的给定方法,很容易实现这一点 never()
,但我还没有找到完整模拟的解决方案。
我真正想要实现的目标:在测试中验证,没有任何东西被打印到控制台上。jUnit的总体思路是这样的:
private PrintStream systemOut;
@Before
public void setUp() {
// spy on System.out
systemOut = spy(System.out);
}
@After
public void tearDown() {
verify(systemOut, never()); // <-- that doesn't work, just shows the intention
}
PrintStream
有很多方法,我真的不想用单独的验证来验证每一个 - System.err
也是如此......
所以我希望,如果有一个简单的解决方案,鉴于我有一个很好的测试覆盖率,我可以强迫软件工程师(和我自己)在提交更改之前删除他们(我的)调试代码,如System.out.println("Breakpoint#1");
或e.printStacktrace();
。
使用这个:
import static org.mockito.Mockito.verifyZeroInteractions;
// ...
private PrintStream backup = System.out;
@Before
public void setUp() {
System.setOut(mock(PrintStream.class));
}
@After
public void tearDown() {
verifyZeroInteractions(System.out);
System.setOut(backup);
}
verifyZeroInteractions(systemOut);
如评论中所述,这不适用于间谍。
有关大致等效但更完整的答案,请参阅gontard对此问题的回答。
由于原始正确答案verifyZeroInteractions
已被弃用,请改用verifyNoInteractions
:
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
public class SOExample {
@Test
public void test() {
Object mock = mock(Object.class);
verifyNoInteractions(mock);
}
}
你可以尝试一个稍微不同的策略:
private PrintStream stdout;
@Before public void before() {
stdout = System.out;
OutputStream out = new OutputStream() {
@Override public void write(int arg0) throws IOException {
throw new RuntimeException("Not allowed");
}
};
System.setOut(new PrintStream(out));
}
@After public void after() {
System.setOut(stdout);
}
如果您愿意,您可以将匿名类型切换为模拟并按照 Don Roby 的建议进行验证。
解决此问题的一种方法是重构您正在测试的类,以允许注入可用于输出的 PrintStream。 这将允许您对其进行单元测试,而无需依赖System
类的行为。 你可以为这个注入使用包私有构造函数,因为你只会从相应的测试类中使用它。 所以它可能看起来像这样。
public class MyClass{
private PrintWriter systemOut;
public MyClass(){
this(System.out);
}
MyClass(PrintWriter systemOut){
this.systemOut = systemOut;
// ...any other initialisation processing that you need to do
}
}
在类本身中,无论在哪里调用后者,都使用 systemOut 变量而不是 System.out。
现在,在测试类中,创建一个模拟PrintStream
,并将其传递给包私有构造函数,以获取要测试的对象。 现在,您可以从测试中运行您喜欢的任何操作,并使用verify
来检查它们对模拟PrintStream
的影响。