連載
» 2014年09月30日 18時00分 公開

RailsテストフレームワークRSpecのインストールと基本的な使い方、基礎文法開発現場でちゃんと使えるRails 4入門(9)(2/3 ページ)

[著:林慶、監修:山根剛司,株式会社アジャイルウェア]

「サンプル(example)」の定義はGiven、When、Thenを意識して

 では、borrowing_spec.rbに「サンプル(example)」を追加してみましょう。以下の例では「「Borrowing」は「user_id」なしでバリデーションに失敗する」という振る舞いを定義します。

require 'rails_helper'
 
RSpec.describe Borrowing, :type => :model do
  it "isn't valid without user_id" do
    borrowing = Borrowing.new
    borrowing.user_id = nil
    expect(borrowing).not_to be_valid
  end
end

 まず3行目のRSpec.describeでは振る舞いを記述する対象を明らかにしています。4〜8行目の「it」メソッドが「サンプル(example)」であり、ブロックに期待する振る舞いを定義します。

 ここでは変数「borrowing」に「Borrowing」モデルのインスタンスが生成・代入されており、そのuser_id属性が空であると「borrowing」がバリデーションをパスしないことを定義しています。

 7行目の式は振る舞いが定義通りかをチェックする「エクスペクテーション」と呼ばれるRSpecの機能です。これについては後ほど詳しく紹介します。

 6行目でわざわざ「nil」を代入していますが、例えば「Borrowing」モデルが生成時に「user_id」を特定の値で初期化するコールバックを実装された場合でも、定義する振る舞いを保証するためです。

 このように、振る舞いの定義には見落としがちなポイントがあるため「前提となる状況・状態(Given)」「発生するイベント(When)」「その結果どうなるか(Then)」を意識するといいでしょう。

 それでは、この「サンプル(example)」を実行してみましょう。以下のような出力が得られるはずです。

F
 
Failures:
 
  1) Borrowing isn't valid without user_id
     Failure/Error: expect(borrowing).not_to be_valid
       expected #<Borrowing id: nil, user_id: nil, book_id: nil, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/borrowing_spec.rb:7:in `block (2 levels) in <top (required)>'
 
Finished in 0.00586 seconds (files took 1.7 seconds to load)
1 example, 1 failure
 
Failed examples:
 
rspec ./spec/models/borrowing_spec.rb:4 # Borrowing isn't valid without user_id

 この出力によると「Borrowing isn't valid without user_id」という先ほど記述したサンプルで失敗しています。失敗した位置も示されており、ここでは「borrowing_spec.rb」の7行目で発生しています。

 なぜ、この「サンプル(example)」が失敗したのかというと「Borrowing」モデル自体にはまだバリデーションの実装をしていないからです。そこで「app/models/borrowing.rb」を次のように実装します。

class Borrowing < ActiveRecord::Base
  belongs_to :user
  belongs_to :book
 
  validates :user_id, presence: true
end

 これにより「Borrowing」モデルは「user_id」が空のときバリデーションに失敗するようになりました。それでは再度「rspec」コマンドでスペックを実行してください。失敗しなくなっているはずです。

 ここまでがRSpecで振る舞いを定義してチェックするというRailsのテストの基本的な流れになります。

覚えておきたいRSpecの基礎文法

 ここからはRSpecの文法について解説します。文法といってもRSpecはRubyベースの「ドメイン特化言語(DSL)」なのでRubyの文法に従います。ドメイン特化言語とは特定の領域で使われる言語のことであり、RSpecは振る舞いを記述する領域で使われる言語です。

 そのため、RSpecの文法は「振る舞いを記述するための文法」ということになります。

「describe」メソッドで「サンプル(example)」をグループ化

 複数の「サンプル(example)」が関係し合っている場合、その関係を説明して「グループ化」しておくと見通しがよくなります。言い換えれば、あるオブジェクトのサンプルをそのオブジェクトのサンプルグループとしてまとめておくことが「describe」メソッドでできます。

 例えば、次のように「Borrowing」モデルについて「RSpec.describe」メソッドのブロックで複数のサンプルを定義できます。

RSpec.describe Borrowing, :type => :model do
  it "isn't valid without user_id" do
    borrowing = Borrowing.new
    borrowing.user_id = nil
    expect(borrowing).not_to be_valid
  end
 
  it "isn't valid without book_id" do
    borrowing = Borrowing.new
    borrowing.book_id = nil
    expect(borrowing).not_to be_valid
  end
end

 さらに「describe」メソッドは入れ子にしたり、複数含めたりすることもできます。

RSpec.describe Borrowing, :type => :model do
  describe 'without user' do
    it 'is not valid' do
      borrowing = Borrowing.new
      borrowing.user = nil 
      expect(borrowing).not_to be_valid
    end
  end
 
  describe '#overdue?' do
    it "returns false when due_back is tomorrow and attribute is default(today)." do
      borrowing = Borrowing.new(due_back: 1.days.since)
      expect(borrowing.overdue?).to be(false)
    end
  end
end

コンテキストを説明したい場合は「context」

 また、「describe」のエイリアスに「context」があります。上のコードの2行目のようにコンテキストを説明したい場合は「describe」より「context」の方がいいでしょう。

注意!「RSpec 3からのdescribeの変更点」

 余談ですが、ここではトップレベルでの「describe」メソッドはRSpecモジュールから呼び出しています。これができるようになったのはRSpec 3からです。

 RSpec 3より前のバージョンでは拡張された「main」オブジェクトの「describe」メソッドをレシーバなしで呼び出していました。

 RSpec 3からは「main」オブジェクトなどへの拡張を設定でオフにでき、その際エラーにならないようにするためにモジュールから「describe」メソッドが呼ばれています。

 RSpec 3でもトップレベルの「main」オブジェクトから「describe」メソッドを呼び出せますが、ブロックの中からはRSpecモジュールの「describe」を呼び出せないので注意してください。


「it」メソッドで「サンプル(example)」を作る

 サンプルを作るには「it」メソッドを使います。これは振る舞いを説明する文字列を引数にとり、振る舞いの定義をオプションのブロックで行います。

 ブロックなしで呼び出すと、「保留のサンプル」としてログに出力されます。

it "will be deleted when user deleted"
#=> Pending:
      Borrowing will be deleted when user deleted
      # Not yet implemented
      # ./spec/models/borrowing_spec.rb:18

「it」では説明しにくいような場合では「specify」

 また、「it」にもエイリアスの「specify」があります。「it」を主語として引数の文字列に述語を書くことで何のサンプルであるかが分かりやすくなりますが、「it」では説明しにくいような場合では「specify」を使うといいでしょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。