読者です 読者をやめる 読者になる 読者になる

RSpecのまとめ

概要

BDDを行うためのテストフレームワーク

準備

gemのインストール。

$ gem install rspec

初期化

$ rspec -init

これを実行すると spec というディレクトリが直下に出来て、その中に spec_helper.rb ファイルが出来る。rspecのテストファイルを作成する際は必ずこれを require する必要がある。

チュートリアル

(1) テスト対象を決める
今回は、/lib/dog.rb を対象とする。

(2) 失敗するテストを書く
下記のテストファイルを作る。
spec/lib/dog_spec.rb

require "spec_helper"
require "dog"

describe Dog do
  it "is named 'Pochi'" do
    dog = Dog.new
    expect(dog.name).to eq 'Pochi'
  end
end

またテスト対象である Dog クラスもファイルだけ作成しておく。 (でないとテスト実行時にエラーがとなってしまうので)

テストを実行してみます。実行結果は・・

$ rspec spec/lib/dog_spec.rb
F

Failures:

  1) Dog is named 'Pochi'
     Failure/Error: expect(dog.name).to eq 'Pochi'

     NoMethodError:
       undefined method `name' for #<Dog:0x007fcc4f97a8d8>
     # ./spec/lib/dog_spec.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.00061 seconds (files took 0.55266 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/lib/dog_spec.rb:5 # Dog is named 'Pochi'

当然、dogの中身がないので失敗します。
テストが成功するように、dog.rbを修正します。

class Dog
  attr_accessor :name

  def initialize(name="Pochi")
    @name = name
  end
end

再度テストを実行してみる。

$ rspec spec/lib/dog_spec.rb
.

Finished in 0.0074 seconds (files took 0.53777 seconds to load)
1 example, 0 failures

今度は無事成功したのでOK! このテスト作成→失敗→修正→成功までの一連のサイクルがBDD(Behavior Driven Development)の基本的な流れになる。

その他のテスト例

describe User do
  describe '#greet' do
    before do
      @params = { name: 'たろう' }
    end
    context '12歳以下の場合' do
      before do
        @params.merge!(age: 12)
      end
      it 'ひらがなで答えること' do
        user = User.new(@params)
        expect(user.greet).to eq 'ぼくはたろうだよ。'
      end
    end
    context '13歳以上の場合' do
      before do
        @params.merge!(age: 13)
      end
      it '漢字で答えること' do
        user = User.new(@params)
        expect(user.greet).to eq '僕はたろうです。'
      end
    end
  end
end

describe '#greet' doは、Userクラスのgreetメソッドをテストするということ。
また、beforeは、テスト実行前に必要な共通処理を記載する。
contextは、直訳すると「文脈」や「状況」という意味。つまり、なにかしら条件が異なる場合はcontextを使ってテストを分割する。

インスタンス変数の代わりにletを使う

インスタンス変数の代わりに以下のようにletを使うこともできる。

require 'spec_helper'
require 'user'

describe User do
  describe '#greet' do
    let(:params) {{name: 'たろう'}}
    context '12歳以下の場合' do
      before do
        params.merge!(age: 12)
      end
      it 'ひらがなで答えること' do
        user = User.new(params)
        expect(user.greet).to eq 'ぼくはたろうだよ。'
      end
    end
    context '13以上の場合' do
      before do
        params.merge!(age: 13)
      end
      it '漢字で答えること' do
        user = User.new(params)
        expect(user.greet).to eq '僕はたろうです。'
      end
    end
  end
end

let(:params) {{name: 'たろう'}}がそう。{}が二重になってちょっとわかりにくいが、外側がRubyのブロックの{}で、内側がハッシュの{}だ。
let は遅延評価されるので、それが使われる時に初めて初期化される。つまり、上のテストコードを以下のように書き直すことが出来てしまう。

require 'spec_helper'
require 'user'

describe User do
  describe '#greet' do
    let(:user) { User.new(params) }
    let(:params) {{name: 'たろう', age: age}}
    context '12歳以下の場合' do
      let(:age) {12}
      it 'ひらがなで答えること' do
        expect(user.greet).to eq 'ぼくはたろうだよ。'
      end
    end
    context '13以上の場合' do
      let(:age) {13}
      it '漢字で答えること' do
        expect(user.greet).to eq '僕はたろうです。'
      end
    end
  end
end

age は params ハッシュの定義時に使われているが、各context内ではじめて実際の値が代入されている。