【问题标题】:RSpec testing of a multiprocess library多进程库的 RSpec 测试
【发布时间】:2017-10-09 17:06:19
【问题描述】:

我正在尝试测试我正在使用 RSpec 创建的 gem。 gem 的目的是创建队列(使用'bunny')。它将用于在多个服务器上的进程之间进行通信。

但是我找不到关于如何在 RSpec 运行环境中安全地创建进程而不产生多个测试进程(所有显示示例失败和成功)的文档。

这是我希望测试做的:

  • 派生子进程,在队列中等待
  • 从主 RSpec 进程推送消息
  • 消耗子进程的队列
  • 等待孩子停止并获取从每个孩子收到的消息数。

现在我实现了一个简单的案例,孩子只消费一条消息然后停止。

这是我目前的代码:

module Queues
  # Basic CR accepting only jobs of type cmd_line
  class CR
    attr_reader :nb_jobs
    def initialize
      # opening communication pipes
      @rout, @wout = IO.pipe
      @nb_jobs = nil # not yet available.
    end
    def main
      @todo = JobPipe.instance
      job = @todo.pop do |j|
        # accept only CMD_LINE type of jobs.
        j.type == Messages::Job::CMD_LINE
      end
      # run command
      %x{#{job.cmd}}
      @wout.puts "1" # saying that we did one job
    end
    def run
      @pid = Process.fork
      if @pid.nil? then
        # we are in the child
        self.main
        @rout.close
        @wout.close
        exit
      end
    end
    def wait
      @nb_jobs = @rout.gets(nil).to_i
      Process.wait(@pid)
      @rout.close
      @wout.close
      @nb_jobs
    end
  end

  @job = Messages::Job.new({:type => Messages::Job::CMD_LINE, :cmd => "sleep 1" })

  RSpec.describe JobPipe do
    context "one Orchestrator and one CR" do
      before(:each) do
        indalo_queue_pre_configure
      end

      it "can send a job with Orchestrator and be received by CR" do
        cr = CR.new
        cr.run # execute the C.R. process
        todo = JobPipe.instance
        todo.push(@job)
        nb_jobs = cr.wait
        expect(nb_jobs).to eql(1)
      end
    end

    context "one Orchestrator and severals CR" do
      it 'can send one job per CR and get all back' do
        crs = Array.new(rand(2..10)) { CR.new }
        crs.each do |cr|
          cr.run
        end
        todo = JobPipe.instance
        crs.each do |_|
          todo.push(@job)
        end
        nb_jobs = 0
        crs.each do |cr|
          nb_jobs += cr.wait
        end
        expect(nb_jobs).to eql(crs.length)
      end
    end
  end

end

编辑:问题是(抱歉没有马上提出,这是一个错误):

有没有办法在多进程环境中正确使用 RSpec?

我不是在寻找代码审查,只是想展示一个我想做的清晰示例。这里我使用了 fork,但是这重复了所有过程(包括 RSpec 部分)并且得到了许多 RSpec 输出,这不是我们在测试套件中所期望的。

我希望只有主程序声明 RSpec 输出/统计数据,而子进程只是与之交互。

我认为正确执行此操作的唯一方法不是分叉,而是通过其他方式调用子进程。也许我一个人回答这个问题......

但不太了解 RSpec,我想知道是否有人知道如何在 RSpec 中做到这一点而无需编写外部代码。在我看来,将单独的代码链接到单个测试示例并不是一个好主意。

我发现关于多进程测试的内容是this plugin 到 RSpec。唯一的问题是我不知道模拟概念,但也许我必须了解它......

【问题讨论】:

  • 那么,你的问题是什么?
  • 欢迎来到stackoverflow! =) Stackoverflow 是一个独特的网站,因为它只提供问题和答案。看起来你已经开始了一个好的程序,但不清楚你在问什么。当您发布问题时,它应该包含一个非常明确的问题,而您目前没有。当您要求社区为您编写代码时,人们可能会误解这一点:这绝对是不受欢迎的。如果您只是在寻找有关您的程序的一些反馈,您可能想尝试codereview.stackexchange.com。再次欢迎!
  • 我编辑了我的问题,希望它更清楚。

标签: ruby rspec multiprocess


【解决方案1】:

好的,我找到了一个答案,即使用 Process.fork 方法的 &block 参数。在这种情况下,您并没有真正复制所有进程,而只是在另一个进程中执行代码块,然后返回 0(如 Ruby doc 中所说)。

这会阻止孩子获得所有 RSpec 环境并多次显示您的测试状态。

PS : 注意不要忘记重定向子进程的 STDOUT/STDERR,如果你不希望它们污染测试的 STDOUT/STDERR。

PS2:如果您在其中调用 @rout.gets(nil),请不要忘记在父端关闭 @wout,因为在父端打开它会阻止 EOF 发生(我提供的代码中的一个错误)即使你把它关在孩子身上。

PS3:使用两个管道而不是一个管道来防止孩子/父母同时说话和听。童年的错误,但我又犯了。

PS4:使用exit 语句(在 &block 的末尾)来防止子进程的僵尸状态,并确保父进程不会等待太长时间以使子进程的其余部分死亡。

抱歉发了这么长的帖子,但它对我来说也很好^^

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    相关资源
    最近更新 更多