如何不使用Mock从JUnit测试命令行Java应用的main方法
测试方案实现
这里提供两种实现方案,你可以根据实际需求选择:
方案1:独立JVM进程执行(推荐,完全满足隔离要求)
该方案会启动单独的JVM进程运行main方法,全局日志配置只会在子进程内生效,完全不会影响测试进程以及其他测试用例,main方法执行逻辑和生产运行完全一致。
测试代码示例(JUnit 5):
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class FooTest { @Test void testMainInSeparateJvm() throws Exception { // 构造JVM启动命令,自动继承当前测试进程的类路径 ProcessBuilder pb = new ProcessBuilder( System.getProperty("java.home") + "/bin/java", "-cp", System.getProperty("java.class.path"), "Foo" // 若Foo有包名,需要写全限定类名 // 如需给main方法传参,可在后面依次添加参数字符串 ); // 合并标准错误和标准输出,方便统一捕获 pb.redirectErrorStream(true); Process process = pb.start(); // 读取控制台输出内容 StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8) )) { String line; while ((line = reader.readLine()) != null) { output.append(line).append(System.lineSeparator()); } } // 等待进程执行结束,校验退出码 int exitCode = process.waitFor(); assertEquals(0, exitCode, "main方法执行异常退出"); // 断言输出符合预期,替换为你实际的校验逻辑 assertTrue(output.toString().contains("预期的输出内容")); } }
方案2:同进程执行+输出捕获(执行速度更快)
如果不需要强隔离日志配置,可以选择该方案,执行效率更高,不需要启动新进程。
测试代码示例(JUnit 5):
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.util.logging.LogManager; public class FooTest { @Test void testMainWithOutputCapture() { // 备份原始系统输出 PrintStream originalOut = System.out; PrintStream originalErr = System.err; try { // 替换系统输出,捕获打印内容 ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream captureStream = new PrintStream(baos, true, StandardCharsets.UTF_8); System.setOut(captureStream); System.setErr(captureStream); // 直接调用main方法,有参数可传入对应字符串数组 Foo.main(); // 获取捕获的输出内容 captureStream.flush(); String output = baos.toString(StandardCharsets.UTF_8); // 断言输出符合预期 assertTrue(output.contains("预期的输出内容")); } finally { // 恢复原始系统输出 System.setOut(originalOut); System.setErr(originalErr); // 重置全局日志配置,不同日志框架重置方式不同,这里是JUL的重置逻辑 LogManager.getLogManager().reset(); } } }
内容的提问来源于stack exchange,提问作者yegor256
相关产品推荐
相关产品推荐

