Minitestで「forkしたRubyのサブプロセスのIO」をテストする


はじめに

この記事を書いている人も完全には理解していないが、一応解決したので、似たような状況で困っている人のヒントになるように記事を残す。

バインディングを作っている時に、「有る操作を行うと、バインディングのC側のグローバル変数みたいなものが変更されてしまうため、後続のテストに影響を与えないようにフォークしてテストを行いたい」というような面倒なケースがある。

Minitestには、assert_outputという便利なメソッドがあり、stdoutやstderrのテストができる。しかし、これらはサブプロセスのIOまでは掌握してくれなかったりする。

かといって、spawn使って bundle exec ruby -r minimap2 -e "hogefuga" みたいなのを書くというのも嫌な感じがする。なにしろこの場合、bundlerを使わないとテストが成功しない。それはどうなのか。

capture_subprocess_io を使う

うまい解決策はないだろうか? 実は Minitestの assert_output のドキュメントを見ると

NOTE: this uses capture_io, not capture_subprocess_io.

という注釈がある。どうやら、サブプロセスのIOを把握する方法もあるけれども、それは遅いから assert_output では使ってないよということらしい。そこでcapture_subprocess_io を利用する。これとForkを組み合わせることで、サブプロセスの実行するプロセスのIOのテストが可能になる。こんな感じである。

  def test_execute_with_string_arg
    out, err = capture_subprocess_io do
      pid = fork do
        MM2.execute('--version') # ここの操作をフォークして行いたい
      end
      Process.waitpid(pid)
    end
    assert_equal "2.24-r1122\n", out
    assert_equal "", err
  end

この記事は、やや適当な記事で、技術的には間違っている部分もあるかもしれないが、ネットでググってもあまりめぼしい情報がヒットしないので記事にした。間違っている場合は、コメント欄で教えてください。

この記事は以上です。