You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编写涉及Signal Trap与Process.kill的测试用例仅在CI环境失败,如何解决?

处理Signal Trap与Process.kill的CI测试问题

问题背景

我为一个进程关闭监听模块编写测试用例,本地环境运行正常,但在GitHub CI中执行测试时失败。推测原因是测试中使用的Process.kill指令直接终止了CI作业,日志中可见'terminated'相关记录。使用版本:

  • ruby 2.7.8
  • rails 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:17:34