如何为带-i交互式选项的Bash脚本编写测试?(现有Java集成测试)
如何用Java测试Bash脚本的交互模式(-i选项)
当然可以测试这类交互场景,核心是模拟用户的输入序列传递给脚本进程,同时捕获脚本的输出和退出状态来校验逻辑是否符合预期。下面给你几种在Java集成测试里的可行方案:
方案一:用Java原生ProcessBuilder实现(无额外依赖)
Java自带的ProcessBuilder API就能直接控制进程的输入输出流,完全满足需求,不需要引入第三方库。
示例代码(JUnit 5)
import org.junit.jupiter.api.Test; import java.io.*; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.*; public class InteractiveScriptTest { @Test void testValidInputFlow() throws IOException, InterruptedException { // 启动带-i参数的脚本进程 ProcessBuilder pb = new ProcessBuilder("/bin/bash", "/path/to/your/script.sh", "-i"); pb.redirectErrorStream(true); // 把stderr合并到stdout,方便统一读取 Process process = pb.start(); // 拿到进程的输入流(模拟用户输入)和输出流(读取脚本输出) try (OutputStream scriptInput = process.getOutputStream(); BufferedReader scriptOutput = new BufferedReader(new InputStreamReader(process.getInputStream()))) { // 模拟用户输入:每个输入后加\n,对应终端的回车操作 String userInputs = "valid_username\ncorrect_password\n"; scriptInput.write(userInputs.getBytes()); scriptInput.flush(); // 确保输入被传递给脚本 // 读取脚本的全部输出 StringBuilder outputBuilder = new StringBuilder(); String line; while ((line = scriptOutput.readLine()) != null) { outputBuilder.append(line).append("\n"); } // 设置超时,避免测试无限挂起 boolean finished = process.waitFor(10, TimeUnit.SECONDS); assertTrue(finished, "脚本执行超时"); // 校验退出码和输出内容 assertEquals(0, process.exitValue()); assertTrue(outputBuilder.toString().contains("登录成功")); assertTrue(outputBuilder.toString().contains("开始处理任务")); } } @Test void testInvalidInputRetry() throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder("/bin/bash", "/path/to/your/script.sh", "-i"); pb.redirectErrorStream(true); Process process = pb.start(); try (OutputStream scriptInput = process.getOutputStream(); BufferedReader scriptOutput = new BufferedReader(new InputStreamReader(process.getInputStream()))) { // 模拟先输错,再输入正确内容的场景 String userInputs = "wrong_user\ninvalid_pwd\nvalid_username\ncorrect_password\n"; scriptInput.write(userInputs.getBytes()); scriptInput.flush(); StringBuilder outputBuilder = new StringBuilder(); String line; while ((line = scriptOutput.readLine()) != null) { outputBuilder.append(line).append("\n"); } process.waitFor(10, TimeUnit.SECONDS); assertEquals(0, process.exitValue()); // 校验脚本是否提示了错误信息 assertTrue(outputBuilder.toString().contains("用户名不存在,请重新输入")); assertTrue(outputBuilder.toString().contains("密码错误,请重新输入")); assertTrue(outputBuilder.toString().contains("登录成功")); } } }
方案二:用Apache Commons Exec简化操作
如果觉得原生API写起来太繁琐,可以用Apache Commons Exec库,它封装了进程控制的细节,处理输入输出更简洁。
步骤1:添加Maven依赖
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-exec</artifactId> <version>1.3</version> <scope>test</scope> </dependency>
示例代码
import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.PumpStreamHandler; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import static org.junit.jupiter.api.Assertions.*; public class InteractiveScriptCommonsTest { @Test void testInteractiveScenario() throws Exception { // 构建命令行 CommandLine cmd = CommandLine.parse("/bin/bash /path/to/your/script.sh -i"); DefaultExecutor executor = new DefaultExecutor(); executor.setExitValue(0); // 预期正常退出码为0 executor.setTimeout(10000); // 设置10秒超时 // 模拟用户输入序列 String inputSequence = "test_email@example.com\n123456\n"; ByteArrayInputStream stdin = new ByteArrayInputStream(inputSequence.getBytes()); ByteArrayOutputStream stdout = new ByteArrayOutputStream(); // 绑定输入输出流 PumpStreamHandler streamHandler = new PumpStreamHandler(stdout, null, stdin); executor.setStreamHandler(streamHandler); // 执行命令 int exitCode = executor.execute(cmd); // 校验结果 String output = stdout.toString(); assertEquals(0, exitCode); assertTrue(output.contains("邮箱格式验证通过")); assertTrue(output.contains("注册完成")); } }
关键注意事项
- 输入必须加换行符:Bash的
read命令默认读取到换行符为止,所以每个模拟输入后面一定要加\n,否则脚本会一直等待输入。 - 关闭输出缓冲:有些脚本会开启输出缓冲,导致Java端无法及时读取输出。可以在启动脚本时用
stdbuf -o0关闭缓冲,比如命令改为/bin/bash -c "stdbuf -o0 /path/to/your/script.sh -i"。 - 超时处理:一定要给进程设置超时时间,避免因为脚本逻辑问题导致测试无限挂起。
- 覆盖多场景:要测试有效输入、无效输入、空输入、边界值等各种情况,确保脚本的校验逻辑都被覆盖到。
内容的提问来源于stack exchange,提问作者cyyy
相关产品推荐
相关产品推荐

