如何使用Mockito(JUnit)为无返回值方法编写测试用例?
别担心,我来帮你搞定这个无返回值方法的测试!首先咱们先把你的代码格式化一下,看起来更清晰:
import java.util.concurrent.TimeUnit; public class Calculation { public void logTimeTaken(String label, long estimatedTime, int size, boolean isDebug) { String out = label + " took " + TimeUnit.MILLISECONDS.convert(estimatedTime, TimeUnit.NANOSECONDS) + " milliseconds for " + size + " events!"; if (isDebug) { System.out.println(out); } else { System.out.println(out); } } }
这个方法的核心行为是生成格式化的日志字符串,并通过System.out.println输出——虽然你的代码里isDebug的两个分支逻辑完全一样,但咱们还是按现有逻辑来写测试。
测试思路
因为方法没有返回值,我们需要验证它的副作用:也就是是否按预期调用了System.out.println,并且传入的参数是正确的格式化字符串。这里有两种常用的实现方式:
方式一:用Mockito静态Mock验证方法调用
从Mockito 3.4.0开始,支持静态方法的Mock,不过需要额外引入mockito-inline依赖。
1. 先准备依赖(Maven示例)
<dependencies> <!-- JUnit 5 核心依赖 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <!-- Mockito 核心 + 静态Mock支持 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> </dependencies>
2. 编写测试类
import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; public class CalculationTest { @Test void logTimeTaken_ShouldPrintCorrectMessage_WhenDebugIsEnabled() { // 1. 准备测试数据 String testLabel = "数据同步"; long testNanos = 2_000_000_000; // 2秒,转成毫秒是2000 int testSize = 200; boolean isDebug = true; // 2. 计算预期的输出字符串 String expectedOutput = testLabel + " took " + TimeUnit.MILLISECONDS.convert(testNanos, TimeUnit.NANOSECONDS) + " milliseconds for " + testSize + " events!"; // 3. 静态Mock System类,用try-with-resources自动关闭Mock try (MockedStatic<System> mockedSystem = mockStatic(System.class)) { // 实例化被测试类 Calculation calculation = new Calculation(); // 调用待测试方法 calculation.logTimeTaken(testLabel, testNanos, testSize, isDebug); // 4. 验证System.out.println是否被调用了1次,且参数是预期的字符串 mockedSystem.verify(() -> System.out.println(expectedOutput), times(1)); } } @Test void logTimeTaken_ShouldPrintCorrectMessage_WhenDebugIsDisabled() { String testLabel = "文件导出"; long testNanos = 800_000_000; // 0.8秒,转成毫秒是800 int testSize = 80; boolean isDebug = false; String expectedOutput = testLabel + " took " + TimeUnit.MILLISECONDS.convert(testNanos, TimeUnit.NANOSECONDS) + " milliseconds for " + testSize + " events!"; try (MockedStatic<System> mockedSystem = mockStatic(System.class)) { Calculation calculation = new Calculation(); calculation.logTimeTaken(testLabel, testNanos, testSize, isDebug); // 因为你的代码里else分支也执行println,所以同样验证调用 mockedSystem.verify(() -> System.out.println(expectedOutput), times(1)); } } }
方式二:捕获System.out输出流验证(无需Mockito)
如果不想用静态Mock,也可以通过重定向System.out的输出流来捕获内容,直接验证输出是否符合预期:
import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculationTest { @Test void logTimeTaken_ShouldOutputCorrectContent() { // 1. 保存原始的System.out,测试后恢复 PrintStream originalOut = System.out; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); System.setOut(new PrintStream(outputStream)); try { String testLabel = "数据处理"; long testNanos = 1_500_000_000; int testSize = 150; Calculation calculation = new Calculation(); calculation.logTimeTaken(testLabel, testNanos, testSize, true); // 注意:println会自动添加换行符,所以预期字符串要加\n String expectedOutput = testLabel + " took " + TimeUnit.MILLISECONDS.convert(testNanos, TimeUnit.NANOSECONDS) + " milliseconds for " + testSize + " events!\n"; // 验证捕获到的输出是否和预期一致 assertEquals(expectedOutput, outputStream.toString()); } finally { // 恢复原始的System.out,避免影响其他测试 System.setOut(originalOut); } } }
额外提醒
你代码里的isDebug分支逻辑完全相同,可能是笔误(比如else分支应该用日志框架输出而不是System.out)。如果后续调整了分支逻辑,只需要对应修改测试里的验证逻辑即可。
内容的提问来源于stack exchange,提问作者srikanth
相关产品推荐
相关产品推荐

