CI流水线中运行单元测试时如何正确处理外部Jar进程的输出定向
CI流水线中运行单元测试时如何正确处理外部Jar进程的输出定向
嘿,我之前也踩过一模一样的坑!本地用ProcessBuilder调用Jar跑单元测试顺得不行,结果一提交到GitLab CI就直接卡壳失败,折腾了半天才发现问题出在外部进程的输出流处理上。
为啥本地没问题CI就炸?简单说,本地控制台的输出缓冲区足够大,就算你不主动去读外部Jar的输出,进程也能把内容写完然后正常退出;但CI环境的缓冲区小得可怜,当Jar输出的内容填满缓冲区后,进程会一直等着有人来取这些输出,直接就卡住不动了,最后测试要么超时要么直接失败。
给你几个亲测有效的解决办法:
主动消费输出流,避免阻塞
别让外部进程的输出流挂着不管,最好开个线程专门读取标准输出和错误输出。如果觉得分开处理麻烦,可以用ProcessBuilder的redirectErrorStream(true)把错误流合并到标准输出,然后统一读取:val process = ProcessBuilder("java", "-jar", "your-vendor.jar") .redirectErrorStream(true) .start() // 开线程读取输出,防止缓冲区满导致阻塞 Thread { process.inputStream.bufferedReader().useLines { lines -> lines.forEach { line -> // 可以把输出打日志,或者直接忽略 println("Jar输出: $line") } } }.start() // 等待进程结束并获取退出码 val exitCode = process.waitFor() require(exitCode == 0) { "Jar执行失败,退出码: $exitCode" }如果不需要输出,直接重定向到空设备
要是你根本不需要看Jar的输出,直接把输出重定向到/dev/null(Linux/macOS)或者NUL(Windows),这样就不会有缓冲区满的问题了:val process = ProcessBuilder("java", "-jar", "your-vendor.jar") .redirectOutput(ProcessBuilder.Redirect.to(Path.of("/dev/null"))) .redirectError(ProcessBuilder.Redirect.to(Path.of("/dev/null"))) .start() val exitCode = process.waitFor() require(exitCode == 0) { "Jar执行失败,退出码: $exitCode" }
另外还要注意CI环境里的细节:比如Jar文件的路径是不是正确(别用本地的相对路径,最好用CI工作目录下的绝对路径),还有Jar文件有没有执行权限,必要的时候可以加一步chmod +x your-vendor.jar(如果是可执行Jar的话)。
内容来源于stack exchange
相关产品推荐
相关产品推荐

