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 を使用している。
Table of Contents
Open Table of Contents
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.watchr
を watchr
コマンドを使って起動しておく。
$ watchr rails.watchr
これで、spec、もしくは、app 配下に変更を加える度に関連するテストが再実行されるので、よからぬことをやってしまった場合にはすぐに気づくことができる。
後半の watchr の部分は、
を参考にした。