Spork/Zeus/Spring によるテスト実行時間の短縮

2013/10/09

この連載の「はじめに」で予告したように、そろそろ Zeus について書こうかなと思って下調べを始めると、最近は Spring という新顔も登場して人気が出てきていることが分かりました。そこで、Zeus と Spring の両方を紹介しようと思います。

Zeus も Spring も Rails application preloader と総称されるソフトウェアの仲間です。テストを実行する度に Rails アプリケーションをロードするのは時間の無駄なので、サーバとしてプリロードしておき、テストの実行時間短縮を目的としたものです。古くから使われているものとしては、Spork というものがあります。しかし、Spork については簡単に触れるだけにとどめます。

参考文献:

Spork/Zeus/Spring の比較

基本的なデータで3つのソフトウェアを比較してみます。「ダウンロード数/日」は、RubyGems.org に出ている最新版のダウンロード数をリリース日からの経過日数で割った数です。

名称 最初のリリース日 最後のリリース日 ダウンロード数/日 自動リロード機能 JRuby/Windows サポート
Spork 2009/05/30 2013/09/14 1,382 No Yes
Zeus 2012/07/25 2013/05/21 494 Yes No
Spring 2013/02/07 2013/06/21 159 Yes No

Spork はすでに4年以上の歴史を持っています。1年2ヶ月のZeus、8ヶ月のSpringと比較するとかなりの古株です。Spork は、JRuby と Windows をサポートしている点も特長です。

しかし、Zeus と Spring には Rails アプリケーションのソースコードが編集されたときに、自動的に再読み込みしてくれるという重要な長所があります。Mac OS X または Linux を使用している方には Zeus か Spring がお勧めです。

Zeus の使い方

まず、gem コマンドでインストールします:

$ gem install zeus

Gemfile に gem 'zeus' を加えて bundle install する方法でもインストールできますが、この方法は推奨されていません。

次に spec/spec_helper.rb から次のような行を探し、あれば削除します:

require 'rspec/autorun'

Zeus を使わない場合でも、この行はそもそも不要です。ruby -Ispec spec/models/customer_spec.rb のように ruby コマンドから直接実行する場合にだけこの行が必要となります。

設定ファイルを作成します:

$ zeus init

エディタで zeus.json を開くと、初期状態は次のようになっています:

{
  "command": "ruby -rubygems -r./custom_plan -eZeus.go",

  "plan": {
    "boot": {
      "default_bundle": {
        "development_environment": {
          "prerake": {"rake": []},
          "runner": ["r"],
          "console": ["c"],
          "server": ["s"],
          "generate": ["g"],
          "destroy": ["d"],
          "dbconsole": []
        },
        "test_environment": {
          "cucumber_environment": {"cucumber": []},
          "test_helper": {"test": ["rspec", "testrb"]}
        }
      }
    }
  }
}

Cucumber を使わない人は、下から7行目を削除してください。

          "cucumber_environment": {"cucumber": []},

Zeus を使用する際には、ターミナルウィンドウが少なくとも2個必要です。1個のターミナルで Zeus サーバが動き、それ以外のターミナルでテストを実行します。

Zeus サーバを起動します。

$ zeus start

すると、ターミナルに次のように表示されます:

Starting Zeus server
[ready] [crashed] [running] [connecting] [waiting]
boot
└── default_bundle
    ├── development_environment
    │   └── prerake
    └── test_environment
        └── test_helper

Available Commands: [waiting] [crashed] [ready]
zeus rake
zeus runner (alias: r)
zeus server (alias: s)
zeus destroy (alias: d)
zeus generate (alias: g)
zeus console (alias: c)
zeus dbconsole
zeus test (alias: rspec, testrb)

別のターミナルで、テストを実行します:

$ zeus rspec spec/

現行の Sinope のテストを私の PC で実行してみたところ、Zeus 抜きでは 3.67 秒かかりましたが、Zeus 環境下で実行すると 2.58 秒で終わりました。

Spring の使い方

まず、Gemfile に以下のコードを追加します:

group :development do
  gem 'spring'
  gem 'spring-commands-rspec'
end

そして、bundle install コマンドを実行します。

何も準備は要りません。いきなり、テストを実行できます:

$ spring rspec spec/

Zeus の場合と異なり、Spring はバックグランドで黙ってサーバを起動するので、私たちが別ウィンドウで明示的にサーバを起動する必要がないのです。

常に Spring を使って RSpec のテストを実行するのなら、bin ディレクトリの rspec コマンドを書き換えてしまうといいでしょう。次のコマンドを実行すると、自動的に書き換えが完了します:

$ spring binstub rspec

bundle binstub rspec を実行すると bin/rspec の中身が元に戻ってしまうので注意してください。

すると、今後は Spring の存在を意識せずに RSpec のテストを実行できます:

$ bin/rspec spec/

現行の Sinope (この連載で作っている Rails アプリケーション)のテストを私の PC で実行してみたところ、初回はサーバの起動に時間がかかるため 4.03 秒かかりましたが、2回目からは 2.74 秒で終わりました。

Zeus vs. Spring

私の環境においては Zeus の方が若干速かったのですが、Spring の方がインストール時も使用時も手間がかかりませんでした。また、bin/rspec を書き換えてくれる機能も便利です。

長期に渡って使用してみないことには何とも言えませんが、Spring の第一印象は素晴らしいものでした。

とはいえ、結論を出すには時期尚早でしょう。Ruby 業界におけるこの種のツールの栄枯盛衰は非常に激しいので、また半年後にはどうなっているか分かりません。

次回は

次回からは、ここのところ出番のなかった Capybara 先生に再登場いただく予定です。では、また。

[更新] spring と spring-commands-rspec を Gemfile 経由でインストールするように記述を変更しました。(2013/11/11)