RSpec/Capybara -- はじめの一歩

2013/08/15

hello_world

連載第2回目は、プログラマーの古き良き伝統「Hello World」です。RailsアプリケーションのTopページに「Hello World!」というコンテンツを持つ p 要素が存在するかどうかをRSpecとCapybaraでテストします。

本連載のメインの読者としてはRubyとRailsを学習中の方を想定しています。したがって、RubyとRailsはインストール済みであるという前提で話を始めます。Ruby のバージョンは2.0.0で、Railsのバージョンは4.0.0です。オブジェクト、クラス、モジュール、メソッド、インスタンス変数といったRubyの基本的な概念、モデル、ビュー、コントローラ、ルーティングといったRailsの基本的な概念については説明しません。なお、データベース管理システムとしてはMySQLを使用します。おそらく、SQlite3やPostgreSQLやOracleでもうまく行くと思いますが、動作確認はしません。もし動かないケースが見つかりましたら、ご連絡ください。

Railsアプリケーションの作成

では、まずサンプルアプリケーションを作りましょう。名前は、ギリシャの女神の名前を取って sinope とします。

$ rails new sinope -d mysql
$ cd sinope
$ rake db:create
$ rake db:migrate
$ rake db:test:prepare

まだマイグレーションスクリプトが存在しないのに rake db:migrate コマンドを実行したのは、db/schema.rb ファイルを生成するためです。次の、rake db:test:prepare コマンドは、db/schema.rb ファイルを読み取ってテスト環境のためのデータベーススキーマを構築します。

RSpec と Capybara のインストール

次に、RSpec と Capybara をインストールします。

Gemfile の末尾に以下のコードを追加します。

group :test do
  gem 'rspec-rails'
  gem 'capybara'
end

追加された Gem パッケージをインストールします。

$ bundle

bin ディレクトリの下に rspec プログラムを設置します。

$ bundle binstubs rspec-core

続いて、次のコマンドを実行します。

$ rails g rspec:install

このコマンドにより spec ディレクトリが作られ、その下に spec_helper.rb というファイルが生成されます。RSpec の挙動を設定する重要なファイルです。

$ rm -rf test

test ディレクトリ以下は不要なので消しておきます。

Railsジェネレータの設定

rails g コマンドでモデルやコントローラを作成したときに、同時にRSpecのファイルも生成されるように設定します。

config/application.rb を次のように編集します。

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)

module Sinope
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    config.generators do |g|
      g.test_framework = "rspec"
    end
  end
end

config.generators do 以下の3行を挿入しました。テストフレームワークとして RSpec を使うように設定しています。

筆者の場合は、さらに次のように設定しています。

    config.generators do |g|
      g.test_framework = "rspec"
      g.controller_specs = false
      g.helper_specs = false
      g.view_specs = false
    end

私はコントローラ、ヘルパー、ビューのためのテストをほとんど書かないので、デフォルトでは生成しないようにしています。これらの設定は、各自の方針によって取捨選択してください。

私がこれらのテストをあまり書かない理由は、いずれ説明します。

トップページの生成

トップページを表示するための top#index アクションを作ります。

$ rails g controller top index
$ rails s

config/routes.rb を編集します。

Sinope::Application.routes.draw do
  root to: 'top#index'
end

rspec_capybara_02_01

ブラウザで http://localhost:3000/ を開くと、右のような画面になります。

最初のテスト

いよいよ本題です。最初のテストを書きましょう。まず、spec/features ディレクトリを作ります。

$ mkdir spec/features

その下に top_page_spec.rb という新規ファイルを次のような内容で作成します。

require 'spec_helper'

describe 'トップページ' do
  specify '挨拶文を表示' do
    visit root_path
    expect(page).to have_css('p', text: 'Hello World!')
  end
end

このコードの意味について次回説明します。トップページの中に、中身が「Hello World!」であるような p 要素があるかどうかを調べています。

テストを実行します。

$ bin/rspec

すると20行ほどのエラーメッセージが出力されます。その中に次のような箇所があることを確認してください。

Failures:

  1) トップページ 挨拶文を表示
     Failure/Error: expect(page).to have_css('p', text: 'Hello World!')
     Capybara::ExpectationNotMet:
       expected to find css "p" with text "Hello World!" but there were no matches...
     # ./spec/features/top_page_spec.rb:6:in `block (2 levels) in <top (required)>'

そして、app/views/top/index.html.erb を次のように修正します。

<h1>Top#index</h1>
<p>Hello World!</p>

再度、bin/rspec コマンドでテストを実行してください。今度は、エラーメッセージが出ないはずです。メッセージの中に次のような箇所がありますね。

1 example, 0 failures

失敗が 0 個。これがテスト成功の証です。

次回は

今回できなかったテストコードの説明を書く予定です。では、また。

[更新] 次回の話と整合性を取るため、テストコード中の featuredescribe に、scenariospecify に変更しました。また、テストを実行するコマンドを rake から bin/rspec に変更しました。(2013/08/16)

[更新] rake db:migrate コマンドの後に、rake db:test:prepare コマンドを実行する手順を加えました。(2013/08/18)

[更新] 「Railsジェネレータの設定」の項で、編集対象ファイルのパスが間違っていました。app/application.rb ではなく config/application.rb が正しいです。読者の方からご指摘をいただきました。ありがとうございます。(2013/08/28)