jrubyでさくさくrspecを実行する(Nailgun or spork)
JRubyでコード書いてるとrspecを起動するたびに永遠にも等しい待ち時間が苦痛で苦痛で苦痛で。
素のrspecで5.6秒、rspecを実行するようにRakefileを書いた rake test で9.4秒、bundle exec rake test に至っては19.7秒という永遠にも等しい時間を必要とする。こうなると生きるのがつらくなってくる。
で、調べてたらsporkなるものがあるらしいんだけど試してみてもなんかいまいちうまくいかない。(うまく動いた、末尾の追記参照)
んでさらに調べてたら、そもそもjvmの起動コストについてはNailgunという仕組みをつかってパスできるっぽい。世の中のRailsアプリだとRailsまわりのライブラリのロードがさらに辛いらしいが、まあそこは今の自分には関係ないのでパス。jvm起動コストだけなんとかなればいいや。
ということで、簡単。以下のようにする。まずjvmをホストするNailgunサーバを起動。
$ jruby --ng-server NGServer started on all interfaces, port 2113.
続けて別の端末でテストを動かす。
$ jruby --ng -S rspec ...................... Finished in 0.364 seconds 22 examples, 0 failures
はやい!!!!!!
具体的にはこのくらい速くなった。
$ time rspec ...................... Finished in 0.335 seconds 22 examples, 0 failures real 0m5.225s user 0m12.507s sys 0m0.460s $ time jruby --ng -S rspec ...................... Finished in 0.21 seconds 22 examples, 0 failures real 0m1.741s user 0m0.032s sys 0m0.046s
Cool!!!!!!!!!!! はやい!!!!! もうこれでいいよこれでぐれいと!!!!!!
気になる点
rake test すると普通のrspecになってしまうので遅い。が、まあこれは Nailgun Server が立ち上がってるかいちいちチェックするのもめんどくさいので、いいや。分かってれば普段自分では打たないし。あとはまあ、bundlerを経由してないのも気になるとかはある。
(追記) sporkもうまく動いた
こちらのページを大変参考にさせていただきました。ありがとうございます。
Twiwt:Blog / jugyo : spork でサクサク RSpec on Rails3
spork を起動するところまではいっしょ。これはもちろん jruby 経由で起動される。
rubyのコードのloadについてはrailsじゃないので必要なファイルを自分で探してロードするようにする。
# in spec/spec_helper.rb Spork.each_run do # This code will be run each time you run your specs. Dir.glob('./lib/myapp/*.rb').each do |file| load file end end
rspecコマンド自体がjvmの起動コストを払ってきわめて重いので、上述ページのirb経由で起動するというやつをもちろん実行する。イマドキなのでこれを pry にしよう。まず gemspec に依存関係を追加。
Gem::Specification.new do |spec| # 略 spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 2.0" spec.add_development_dependency "spork" spec.add_development_dependency "pry" end
それから pry を起動するためのスクリプト(仮に script/spec_server_pry とする)を作成する。こんな。
#!/usr/bin/env ruby require 'drb' require 'pry' begin begin DRb.start_service("druby://localhost:0") rescue SocketError, Errno::EADDRNOTAVAIL DRb.start_service("druby://:0") end $spec_server = DRbObject.new_with_uri("druby://127.0.0.1:8989") rescue DRb::DRbConnError err.puts "No DRb server is running. Running in local process instead ..." end def rspec(file=nil) if file $spec_server.run(["--color", "--format", "s", file], STDERR, STDOUT) else $spec_server.run(["--color", 'spec'], STDERR, STDOUT) end end puts <<DESC Example: > rspec 'spec/xxx_spec.rb' or (for all tests) > rspec DESC Pry.start
IRBをPryに変えたのと、rspec起動時に対象を指定しない場合は全テストを対象に走らせるようにした。その際はすっきりた見た目で。
こんな感じにしておくと常にpryを起動*1しておき、気が向いたらいつでも rspec と叩くだけでテストが実行される。YATTA!!!!!!!!