Refinery CMS (Rails) でのテスト環境

Written by @dr_taka_n at 2011/05/15 19:01 [, , , ]

Refinery CMS というのは、Rails をベースにした CMS。
公式サイトにもテストに関するガイドはあるのだが、テストを開始する前の準備の記載がないのでメモしておく。

そもそも、Refinery CMS は CMS なので、Refinery CMS 自体のテストを実施する必要があるの?という話もあるかもしれないが、 Refinery CMS は既存の動作を override できたり、通常の Rails アプリの感覚で拡張できたり、はたまた engine の仕組みを使って必要な機能を拡張していける。

いろいろ手をいれていくと新たに追加した部分だけでなく、既存の環境を壊していないよね?と確認したくなるもの。既存環境のテストをできる状態にはしておきたい。

テスト環境の構築には、Refinery CMS 独自の部分と Rails プロジェクト共通の部分とがあり、先に Refinery CMS 独自のセットアップを行い、更に環境のカイゼンのためのセットアップを幾つか行う。

なお、ここで記載している内容は Refinery CMS は 0.9.9.21 を、Rails は v3.0.7 を使用している。

Refinery CMS テスト環境の準備

まずは、Refinery CMS 独自の部分。

Refinery CMS 自体は gem でインストールされ、実行時には gem のライブラリが使用される。作成されたプロジェクト内にその存在を見ることはないが、必要に応じて自身のプロジェクトに必要なものをとってくることになる。

テストの場合も同様で、必要な gem をインストールしておき、自身のプロジェクトでテストを実行するために必要なファイルをセットアップする必要がある。

まずは、Gemfile で必要な gem を読み込むようにする。 Refinery CMS のプロジェクトを作成した際に Gemfile にはデフォルトで既に以下の内容が記述されている。

Gemfile:

group :development, :test do
  # To use refinerycms-testing, uncomment it (if it's commented out) and run 'bundle install'
  # Then, run 'rails generate refinerycms_testing' which will copy its support files.
  # gem 'refinerycms-testing',    '~> 0.9.9.21'
end

refinerycms_testing gem がコメントアウトされているので、有効にしておく。

Gemfile:

group :development, :test do
  gem 'refinerycms-testing',    '~> 0.9.9.21'
end

bundler を実行しておく。

$ bundle install

Gemfile にも書かれていたように、Refinery CMS でテストを実施するために必要なファイルを refinerycms-testing が用意している generator を使って作成しておく。

$ rails g refinerycms_testing
      create  config/cucumber.yml
      create  spec/spec_helper.rb
      create  spec/rcov.opts
      create  .rspec
      create  features/support/paths.rb

rspec を実行する。

$ rake spec
(in /home/taka/Work/refinerycms-test-sample)
...(略)...
No DRb server is running. Running in local process instead ...
...........................................................................................................

Finished in 6.82 seconds
107 examples, 0 failures

107 example が実行され、0 failure という結果。テストも実行でき、結果もオールグリーンなので Refinery CMS 本体のテストはこれで OK。

更にカイゼンする - spork、watchr

ここからは、Refinery CMS 独自というわけでもなく、通常の Rails アプリにも当てはまる部分。

spork を利用してテスト環境の下準備時間を短縮

先ほどのテスト結果を見ると、”Finished in 6.82 seconds” と出ており、107件の example の全てのテストの実行に約7秒かかったことになっているが、実際はもっと時間がかかっている。

確認してみる。

$ time rake spec
(in /home/taka/Work/refinerycms-test-sample)
...(略)...
No DRb server is running. Running in local process instead ...
...........................................................................................................

Finished in 6.88 seconds
107 examples, 0 failures

real    0m32.983s
user    0m25.254s
sys     0m1.788s

30秒ちょっと。短くできるものは短くしておく。

実行時のメッセージで DRb server を探しているが、spork server を上げておけば、rspec の下準備にかかる時間を短縮できる。

refinerycms-testing の generator を使って作成した spec_helper.rb には spork の利用を想定した以下の記載が既に存在する。

spec/spec_helper.rb:

# If spork is available in the Gemfile it'll be used but we don't force it.
unless (begin; require 'spork'; rescue LoadError; nil end).nil?

  Spork.prefork do
    # Loading more in this block will cause your tests to run faster. However,
    # if you change any configuration or code from libraries loaded here, you'll
    # need to restart spork for it take effect.
    setup_environment
  end
  ...

spork を利用するには Gemfile に spork の読み込みを追加してあげる。

group :development, :test do
  gem 'spork'
end

再度 bundle install で bundler を実行しておき、spork を起動しておく。

$ spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!

再度テストを実行する。

$ time rake spec
(in /home/taka/Work/refinerycms-test-sample)
...(略)...
...........................................................................................................

Finished in 6.86 seconds
107 examples, 0 failures

real    0m22.155s
user    0m9.497s
sys     0m0.764s

テスト自体にかかっている時間は先ほどと変わりないが、テストを起動してから終了するまでの実際に掛かっている時間が10秒ほど短縮された。

それでも22秒かかってはいるが、全てのテストを継続的に都度実施しながらコーディングというのはあまりやらないだろう。 着手している機能部分に関連するテストを継続的に行う方がストレスも少なく効率的だ。 それを行うためにもう一手加える。

watchr を使った継続的なテスト

全てのテストを都度実施するのではなく、編集を行ったソースに対して関連するテストを都度、しかも自動的に実施できるとうれしい。

ファイルを監視していて、何か変更があった時にやりたいことを実施させることができる watchr という便利な gem がある。

使い方は、上記の sample や、watchr - ファイルに変更があったら何かする / もしくはオサーンについて - 川o・-・)<2nd life などが参考になる。ここでは詳細は割愛する。

書き方はいろいろあるだろうが、プロジェクトルート直下に rails.watchr というファイルを用意し、今回は以下のような記述を行った。

rails.watchr:

watch('^spec/(.*)_spec\.rb') { |m| run_test_matching(m[1]) } # (1)
watch('^app/(.*)\.rb') { |m| run_test_matching(m[1]) }       # (2)
watch('^spec/spec_helper\.rb') { run_all_tests }             # (3)
Dir['vendor/engines/*'].each do |d|                          # (4)
  watch("^#{d}/spec/(.*)_spec\.rb") { |m| run_test_matching(m[1]) }
  watch("^#{d}/app/(.*)\.rb") { |m| run_test_matching(m[1]) }
end

def all_specs
  Dir['spec/**/*_spec.rb'] + Dir['vendor/engines/**/spec/**/*_spec.rb']
end

def run_all_tests
  @all_tests_passing = run(all_specs.join(' '))
  puts 'All tests pass' if @all_tests_passing
end

def run_test_matching(thing_to_match)
  matches = all_specs.grep(/#{thing_to_match}/i)
  if matches.any?
    @all_tests_passing &= run(matches.join(' '))
    run_all_tests unless @all_tests_passing
  end
end

def run(files_to_run)
  system("clear; rspec --drb #{files_to_run}")               # (5)
end

若干補足する。

(1) では、spec ディレクトリ配下のテストを記述する spec ファイルに変更があった場合には、その spec を実行する。

(2) では、app ディレクトリ配下の全てのファイルを監視し、変更があった場合には、そのファイルに対応する spec を実行する。

(3) では、spec ファイル全てから参照されている helper である spec_helper.rb が変更された場合には全ての spec を実行し直す。

(4) では、追加した全ての engine の spec、及び、app 配下のディレクトリを監視しておき、変更があった場合には該当する spec を実行する。各 engine に対して (1) と (2) でやっているのと同じことを実行していることになる。
ちなみに、watch メソッドへの引数のパターンでうまくパターンを組めれば Dir を使って engine のディレクトリを探索させてやることもないのだが、今回うまくマッチさせられなかったのでこのような書き方になっている。良い書き方を見つけたら変更しておこう。

(5) は rspec の実行部分だが、--drb オプションを使うことで裏で起動している spork を利用するので、待ち時間なく即座にテストが実行される。

用意した rails.watchrwatchr コマンドを使って起動しておく。

$ watchr rails.watchr

これで、spec、もしくは、app 配下に変更を加える度に関連するテストが再実行されるので、よからぬことをやってしまった場合にはすぐに気づくことができる。

後半の watchr の部分は、

を参考にした。

blog comments powered by Disqus