RSpecを並列処理させようとした時の事

2014年8月1日金曜日 Ryusei Naya

皆さん初めまして ヴァル研でソフトウェアエンジニアをやらしてもらってます Naya です。
VAL の LABO にアクセスして頂きありがとうございます。

さっそくですが標題の件です

サービスの開発をしていて、RSpecでテストを書いていたりすると
だんだんテストケースが増えてきて重くなっちゃう事ありますよね。(RSpecに限らずですが)


「じゃあテストケースをマルチスレッドで実行すればいいじゃない」ということになって

parallel_tests を使ったりとか


bundle exec parallel_rspec spec/hogehoge -n 4

とかすると、specファイル毎にいい感じに並列処理してくれますよね。
じゃあ、1個のspecファイルのテストが極端に重い場合はどうしたらいいんだろう?

  「specファイルを複数に分割すればいんじゃね?」

っていう方 
たぶん 正解。

まあ、それで記事が終わっちゃってもアレなんでもうちょっと続けますが

こういう時 specファイル側を変更してdescribe毎に並列処理させたりとかもできたら
ちょっといいかな とか思ったので、ちゃんとできるのか検証してみました。


検証 

OS: OS X 10.9.4
CPUコア数:4
Ruby:2.0.0p195
$ bundle exec gem list

*** LOCAL GEMS ***

bundler (1.3.5)
diff-lcs (1.2.5)
parallel (1.1.2)
rake (10.3.2)
rspec (3.0.0)
rspec-core (3.0.3)
rspec-expectations (3.0.3)
rspec-mocks (3.0.3)
rspec-support (3.0.3)



まずは4つの describe を実行するspecファイルを作成してテストを流します。

sample_spec.rb
# -*- coding: utf-8 -*-

(1..4).each {|value|
     describe "テストケース#{value}" do
          sleep 5
          it "必ず成功するテスト" do
               expect(1).to eq(1)
          end
     end
}


$ bundle exec rake spec SPEC=spec/sample_spec.rb
....

Finished in 0.00129 seconds (files took 20.1 seconds to load)
4 examples, 0 failures


"sleep 5" を4回シーケンシャルに実行するので20秒くらいかかってますね。

では "(1..4).each" の部分を parallel を使ってマルチスレッドで処理するようにしてみます。

スレッド数は4です。


sample2_spec.rb
# -*- coding: utf-8 -*-

require 'parallel'

Parallel.each( (1..4) , in_threads:4) {|value|
     describe "テストケース#{value}" do
          sleep 5
          it "必ず成功するテスト" do
               expect(1).to eq(1)
          end
     end
}


$ bundle exec rake spec SPEC=spec/sample2_spec.rb
....


Finished in 0.00126 seconds (files took 5.1 seconds to load)
4 examples, 0 failures



お 5秒くらいになってる。

じゃあ今度は "sleep 5” を “it” に渡すブロックの中に書いて実行してみます。
また、このブロック内では Ruby上でスリープしている時間は計測されないみたいなので
“system ’sleep 5’ “ に変更します。


sample3_spec.rb
# -*- coding: utf-8 -*-

require 'parallel'


Parallel.each( (1..4) , in_threads:4) {|value|
     describe "テストケース#{value}" do
          it "必ず成功するテスト" do
               system 'sleep 5'
               expect(1).to eq(1)
          end
     end
}


$ bundle exec rake spec SPEC=spec/sample3_spec.rb
....

Finished in 20.01 seconds (files took 0.09787 seconds to load)
4 examples, 0 failures


あれ、、20秒に戻った、、、うーむ



結果

今回の方法でdescribe 毎に並列処理させる事は一応可能、ただし
it に渡しているブロックは describe が定義されるタイミングで実行しているわけじゃないみたいなので、そのブロックの中の処理は並列化できなかった。
なにか良い方法を知っている方がいたら教えてください。


終わり