编写涉及Signal Trap与Process.kill的测试用例仅在CI环境失败,如何解决?
处理Signal Trap与Process.kill的CI测试问题
问题背景
我为一个进程关闭监听模块编写测试用例,本地环境运行正常,但在GitHub CI中执行测试时失败。推测原因是测试中使用的Process.kill指令直接终止了CI作业,日志中可见'terminated'相关记录。使用版本:
ruby 2.7.8rails 7.0.8.4
实现代码
module ProcessShutdownListener def initialize(...) setup_graceful_shutdown super end def shutdown_requested? @_shutdown_requested end private def setup_graceful_shutdown @_shutdown_requested = false Signal.trap("TERM") do graceful_shutdown end Signal.trap("INT") do graceful_shutdown end end def raise_error_on_process_shutdown? false end def graceful_shutdown @_shutdown_requested = true if raise_error_on_process_shutdown? raise ShutdownRequested end end end
测试用例代码
class BasicExample include ProcessShutdownListener def raise_error_on_process_shutdown? false # default end end class AutomaticRaiseErrorExample include ProcessShutdownListener def raise_error_on_process_shutdown? true end end describe ProcessShutdownListener do describe "responding to process shutdown signals" do context "when #raise_error_on_process_shutdown? is false" do subject! { BasicExample.new } describe "#shutdown_requested?" do context "when process receives the TERM Signal" do it "returns true" do Process.kill("TERM", 0) expect(subject.shutdown_requested?).to be true end end context "when process receives the INT Signal" do it "returns true" do Process.kill("INT", 0) expect(subject.shutdown_requested?).to be true end end end end context "when #raise_error_on_process_shutdown? is true" do subject! { AutomaticRaiseErrorExample.new } context "when process receives the TERM Signal" do it "raises an error" do expect { Process.kill("TERM", Process.pid) }.to raise_error(ShutdownRequested) end end end end end
解决方案
GitHub CI环境中,测试进程收到TERM/INT信号后会直接终止整个作业,这是导致测试失败的核心原因。以下是几种可行的测试方案:
1. 用子进程隔离信号影响
通过fork子进程执行信号测试逻辑,避免信号影响到父进程(CI测试进程):
context "when process receives the TERM Signal" do it "returns true" do read, write = IO.pipe pid = fork do read.close subject = BasicExample.new Process.kill("TERM", Process.pid) write.puts(subject.shutdown_requested?.to_s) exit end write.close result = read.gets.chomp == "true" Process.wait(pid) expect(result).to be true end end
2. 直接模拟信号触发逻辑
不发送真实信号,而是直接调用信号处理的逻辑块:
context "when process receives the TERM Signal" do it "returns true" do # 获取TERM信号的处理Proc term_trap = Signal.trap("TERM", nil) Signal.trap("TERM", term_trap) # 恢复原信号处理 term_trap.call # 直接触发处理逻辑 expect(subject.shutdown_requested?).to be true end end
3. 直接调用核心处理方法
对于需要验证异常抛出的场景,可直接调用graceful_shutdown方法,绕开发送信号的步骤:
context "when process receives the TERM Signal" do it "raises an error" do expect { subject.graceful_shutdown }.to raise_error(ShutdownRequested) end end
内容的提问来源于stack exchange,提问作者queroga_vqz
相关产品推荐
相关产品推荐

