「Everyday Rails - RSpecによるRailsテスト入門」を写経してみた

The RSpec BookやRailsチュートリアルを終えたタイミングでちょうど Everyday Railsの翻訳本が出たため、今度はこちらを手にとって写経をしてみました。
Everyday Rails - RSpecによるRailsテスト入門 - Leanpub



結論としては、RailsRSpecを独学で学んでいる初心者に対してとても分かりやすい本で、かつ、今後もバージョンアップに追随していく予定というお得なものでもあったため、購入して正解でした。



感想

原著者のAaron Sumnerさんも手を動かして学ぶことを推奨していることからも、本書を通して写経しやすい構成になっていました。特に、アプリコードにはほとんど手を入れずにテストコードを書き進める形であったため、テストの書き方を学ぶのに集中することができました*1


他に、RSpecに対する便利な設定方法や詳しく知りたい場合のリンク先も紹介されているため、コンパクトであるにも関わらずRSpecを学習してゆく上で良いとっかかりを得られる本でした。


翻訳に関しても、ベータ版の段階でもそれほど大きな違和感を持つことなく、読み進めることができました。
また、@さんをはじめとする翻訳者の方々から次々と進捗状況が出ていたので、どんなものかワクワクしながら発売を待ったりしていました。




なお、書籍の中で示されているけれど自分ではやらなかったこととしては、

  • spec/spec_helper.rbにconfig.include FactoryGirl::Syntax::Methodsを追加すること
    • 後から振り返った時に、FactoryGirl由来のメソッドかどうか、分かりやすくするため
  • expect(..).toの否定形は、「expect(..).to_not」を使うこと

といったところです。



写経環境

今回もWindows上ではなく、VirtualBoxVM上で行いました。

  • Ubutu12.04
  • rbenv
  • Ruby 2.0.0p353(写経した当時の最新版)


具体的な環境は、前回のエントリのものなります(なお、Rubyはp353を指定していました。ただ、p451でRSpecを動作させてもエラーにはなりませんでした)。


また、参考までに自分の写経したコードをGitHubにあげておきました。
一番最初のベータ版(2014/2/7発行)を使って写経したため、現在の正式版とは異なるかもしれません。
thinkAmi/everyday_rails_4_0-syakyo · GitHub



写経を進めるにあたり

サンプルコードのclone

Everyday Railsのコードは公開されていますが、テストコードが付属していないブランチ「01_notest」のみをチェックアウトしたかったため、single-branchオプションを利用しました。

git clone -b 01_untested --single-branch https://github.com/everydayrails/rspec_rails_4.git


なお、single-branchオプションは1.7.10以降のバージョンで利用できるのですが、Ubuntu12.04のGitは1.7.9.5であったため、今回はPPAのものを使いました。
参考:git - Clone only one branch - Stack Overflow



自分のGitHubへのpush

git cloneしたものをpushしながら進めるため、以下のようにして自分のGitHubへpushしました。

vagrant@ ~/everydayrails_4_0/rspec_rails_4 (01_untested) $ git remote
origin
vagrant@ ~/everydayrails_4_0/rspec_rails_4 (01_untested) $ git remote add github https://github.com/thinkAmi/everyday_rails_4_0-syakyo.git
vagrant@ ~/everydayrails_4_0/rspec_rails_4 (01_untested) $ git remote -v
github  https://github.com/thinkAmi/everyday_rails_4_0-syakyo.git (fetch)
github  https://github.com/thinkAmi/everyday_rails_4_0-syakyo.git (push)
origin  https://github.com/everydayrails/rspec_rails_4.git (fetch)
origin  https://github.com/everydayrails/rspec_rails_4.git (push)
vagrant@ ~/everydayrails_4_0/rspec_rails_4 (01_untested) $ git push -u github 01_untested
Username for 'https://github.com': thinkAmi
Password for 'https://thinkAmi@github.com': 
Counting objects: 250, done.
Compressing objects: 100% (190/190), done.
Writing objects: 100% (250/250), 39.14 KiB | 0 bytes/s, done.
Total 250 (delta 71), reused 154 (delta 36)
To https://github.com/thinkAmi/everyday_rails_4_0-syakyo.git
 * [new branch]      01_untested -> 01_untested
Branch 01_untested set up to track remote branch 01_untested from github.

参考: transitive.info - git remote 使い方



悩んだところなどのメモ

bundle installしようとしたら、エラー

p7にて、git cloneしたGemfileを元にbundle installしたところ、以下のエラーが発生しました。

vagrant@ ~/everyday_rails_4_0-syakyo (01_untested *) $ bundle exec rspec
rbenv: version `2.0.0' is not installed
rbenv: version `2.0.0' is not installed


原因は、.ruby-versionファイルに「2.0.0」とだけ記載されていて、パッチレベルが記載されていなかったため、rbenvで利用するときにエラーとなったようでした。
参考:octopress をアップデートした (.ruby-version が conflict した) 話 - @znz blog


そのため、.ruby-versionファイルにパッチレベルを記載しました。

ruby2.0.0-p353


また、.gitignoreにも .ruby-version を無視するように追加しておきました。

git rm --cached .ruby-version

参考:gitignoreまとめ - maeharin log



p30 「アプリケーションにファクトリを追加する」に伴う作業

p31で、example「it "has a valid factory"」を追加する際、不要になったexample「it "is valid with a firstname, lastname and email"」を削除しておきます。


また、以下の部分をファクトリを使うように修正しておきます。修正を忘れると、p37のコールバックを追加したところでexampleが失敗するようになりました。

  • 修正前
describe "fileter last name by letter" do
    before :each do
      @smith = Contact.create(firstname: 'John',
                              lastname:  'Smith',
                              email:     'jsmith@example.com')
      @jones = Contact.create(firstname: 'Tim',
                              lastname:  'Jones',
                              email:     'tjones@example.com')
      @johnson = Contact.create(
                              firstname: 'John',
                              lastname:  'Johnson',
                              email:     'jjohnson@example.com')
    end
    ...
  • 修正後
describe "fileter last name by letter" do
    before :each do
      @smith = FactoryGirl.create(:contact,
                                  lastname:  'Smith',
                                  email:     'jsmith@example.com')
      @jones = FactoryGirl.create(:contact,
                                  lastname:  'Jones',
                                  email:     'tjones@example.com')
      @johnson = FactoryGirl.create(:contact,
                                    lastname:  'Johnson',
                                    email:     'jjohnson@example.com')
    end
    ...
p79 scenarioに js: true を追加したexampleを実行するとエラー

これは自分の環境由来のものなので、他の環境では発生しないかもしれませんが、2つの原因により発生しました。


1. /etc/hosts ファイル由来
自分がVagrantで環境を作ったせいかもしれませんが、実行するとしばらく動作が停止した後、以下のエラーが発生しました。

Failures:

  1) About BigCo model toggles display of the model about display
     Failure/Error: visit root_path
     Selenium::WebDriver::Error::WebDriverError:
       unable to bind to locking port 7054 within 45 seconds


原因は、/etc/hosts ファイルの設定内容に問題があったためです。
参考:Ruby - unable to bind to locking port 7054 within 45 seconds (Selenium::WebDriver::Error::WebDriverError) - Stack Overflow


そのため、以下の通りに、/etc/hosts ファイルを修正します。
修正前

127.0.0.1       everydayrails-4.0 everydayrails-4
127.0.1.1       everydayrails-4.0 everydayrails-4


修正後

127.0.0.1       localhost
127.0.1.1       everydayrails-4.0 everydayrails-4


2. selenium-webdriver gemのバージョン由来
写経環境のFirefoxを26にアップデートした後にRSpecでテストをしたところ、Firefoxは起動するものの何も動作せず、以下のエラーが発生しました。

1) About BigCo model toggles display of the model about display
     Failure/Error: visit root_path
     Selenium::WebDriver::Error::WebDriverError:
       unable to obtain stable firefox connection in 60 seconds (127.0.0.1:7055)


原因は、現時点のGitHub上の最新コミット、および自分が写経したベータ版(2014/2/7版)では、Gemfileで「gem "selenium-webdriver", "~> 2.35.1"」と古いバージョンをしているためです。
参考:selenium - unable to obtain stable firefox connection in 60 seconds (127.0.0.1:7055) ubuntu - Stack Overflow


そのため、上記のリンクにあるように「2.38.0」と指定するか、正式版のp7に記載されている「2.39.0」を指定すれば、正しく動作するようになりました。

*1:RailsチュートリアルでもRSpecを使ったテストコードを書きましたが、その他にもアプリコードを書いていました。

*2:http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax#comment-558872533

*3:Vagrantfileを見返したら、config.vm.hostname = "everydayrails-4.0" となっていることに気づきました...