【RSpec】RSpecの基本を0から学んでみた。

RubyやRails開発をしていると一度は目にしたことがある「RSpec」ですが、その存在は認知していたものの、時間を取って体系的に勉強したことがなかったので今回の記事を機会に一度まとめて見ようと思い本記事を執筆しています。

現場等に入らないと実際の所使う機会は無いですし、特に初学者だとテストは後回しにしがちなので滅多に触れることは無いでしょう。しかしテストが出来るのと出来ないのではスキルもそうですが適切な場面において適切な開発が出来ず、開発効率に差が出てしまうのも事実です。

「0から」とタイトル通り、今回はその「RSpec」について基本中の基本を初めからまとめていこうかと思います。

スポンサードリンク

RSpecとは

そもそもRSpecとは何なのでしょうか。公式サイトによると、プログラムのふるまいを記述するためのドメイン特化言語 (DSL) を提供するフレームワークとして説明されています。これまでのRailsのテスト駆動開発で使われてきた「Test::Unit」を代替するフレームワークでもあります。以前のRailsに触れたことがないので僕はここら辺の変遷があまり実感できないのですが、とりあえず簡単に言うと「プログラムの動きを確認するためのフレームワーク」みたいです。

ドメイン特化言語(DSL)とは、特定のタスク向けに設計されたコンピュータ言語の事です。テスト専用みたいなイメージを持ってもらえれば大丈夫かと思います。

現在Rails開発においてはRSpecのスキルが「必須」としている現場も多いようで、Railsエンジニアになるためにはどうやら避けては通れない道の様です。

TDD(テスト駆動開発)について

RSpecはTDDにおいて利用します。TDDとはテストファーストな開発方法であり、以下の様な手順で開発を進めます。

1.テストコードをプログラムの意図に反する様に書き、それが失敗する事を確認する(レッド)

2.テストコードをプログラムの期待した結果に沿う様に書き、それが成功することを確認する(グリーン)

3.レッドにならないように、再度プログラムのリファクタリングを行う

TDDの目指すところは綺麗な実装できちんと動くコードを記述することです。レッドグリーンリファクタリングの繰り返しを行うことでコードを洗練していきます。レッドではプロダクトコードに何も書かれていなくても、とにかく先にテストコードを書き、それが失敗する事を確認します。

RSpecのインストールと実行方法

以下のコマンドで簡単にインストールできます。

gem install rspec

このように表示されたらインストール完了です。

Fetching: rspec-support-3.8.0.gem (100%)
Successfully installed rspec-support-3.8.0
Fetching: rspec-core-3.8.0.gem (100%)
Successfully installed rspec-core-3.8.0
Fetching: diff-lcs-1.3.gem (100%)
Successfully installed diff-lcs-1.3
Fetching: rspec-expectations-3.8.1.gem (100%)
Successfully installed rspec-expectations-3.8.1
Fetching: rspec-mocks-3.8.0.gem (100%)
Successfully installed rspec-mocks-3.8.0
Fetching: rspec-3.8.0.gem (100%)
Successfully installed rspec-3.8.0
Parsing documentation for rspec-support-3.8.0
Installing ri documentation for rspec-support-3.8.0
Parsing documentation for rspec-core-3.8.0
Installing ri documentation for rspec-core-3.8.0
Parsing documentation for diff-lcs-1.3
Couldn't find file to include 'Contributing.rdoc' from README.rdoc
Couldn't find file to include 'License.rdoc' from README.rdoc
Installing ri documentation for diff-lcs-1.3
Parsing documentation for rspec-expectations-3.8.1
Installing ri documentation for rspec-expectations-3.8.1
Parsing documentation for rspec-mocks-3.8.0
Installing ri documentation for rspec-mocks-3.8.0
Parsing documentation for rspec-3.8.0
Installing ri documentation for rspec-3.8.0
Done installing documentation for rspec-support, rspec-core, diff-lcs, rspec-expectations, rspec-mocks, rspec after 10 seconds
6 gems installed

RSpecは、rspecコマンドの引数としてふるまいを定義したスペックファイルを指定すれば、そのRSpecの実行が可能です。

rspec (RSpecのファイル名).rb

RSpecのシンタックス

それでは、実際にRSpecの記述方法についてまとめていこうと思います。

(今回RSpecのシンタックスをまとめるにあたって伊藤さんのQiitaの記事を参考にさせて頂きました。大変参考になりました、ありがとうございます。)

describe / it / expect

RSpecの最も基本的な構成要素は以下の様に記述できます。

describe 'test of add' do
  it '1 + 1 equal to 2' do
    expect(1 + 1).to eq 2
  end
end

「describe」でテストを1つのグループとして定義します。そしてその中に、「it」を含むブロックを記述しています。この「it」の役割はexampleと呼ばれる単位で1つのまとまりを作成しています。itブロックの中が全て期待した通りの動きであれば、そのexampleはパス(成功)したことになります。

expect(1 + 1).to eq 2

この部分がエクスペクテーションと呼ばれる文で、ここで期待する結果を検証します。

ここで書かれているメソッドの様なふるまいをする「to」や「eq」はマッチャと呼ばれるもので、期待値と実際の値を比較して、一致した又は一致しなかったという結果を返すというオブジェクトのことです。true及びfalseを返すメソッドのようなものだというイメージを持つと良いかもしれません。適当に名前を付けて、このテストコードを実行してみましょう。

>rspec test_rspec.rb
.
Finished in 0.01562 seconds (files took 0.38503 seconds to load)
1 example, 0 failures

このようにテストコードに1つのexampleが含まれており、かつそのテストコードにfail(失敗)が無い事が確認できます。

describeの中に複数のexampleを持つことも可能です。

describe 'test of operator' do
  it '1 + 1 equal to 2' do
    expect(1 + 1).to eq 2
  end
  it '3 - 1 equal to 2' do
    expect(3 - 1).to eq 2
  end
end

ちなみにこのテストコードの実行結果は上と同様に以下の様に表示されます。

Finished in 0 seconds (files took 0.29681 seconds to load)
2 examples, 0 failures

さらに、exampleの中に複数のエクスペクテーションを記述する事も可能です。

describe 'test of add' do
  it '1 + 1 equal to 2' do
    expect(1 + 1).to eq 2
    expect(1 + 1).to eq 2
    expect(1 + 1).to eq 2
    expect(1 + 1).to eq 2
    expect(1 + 1).to eq 2
  end
end

実行結果は以下の様になります。

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

このように、1exampleのみ検出され、各エクスペクテーションについての詳細な結果は出力されません。よってエクスペクテーションを複数記述する場合は注意が必要です。もしテストがfailした場合、どのエクスペクテーションが原因なのかわからなくなってしまうためです。

ですので、基本的には1つのexampleにつき1つのエクスペクテーションを記述するのが原則だそうです。

そして、describeを入れ子構造にすることも可能です。

describe 'test of operator' do
  describe 'test of add' do
    it '1 + 1 equal to 2' do
      expect(1 + 1).to eq 2
    end
  end
  describe 'test of minus' do
    it '3 - 1 equal to 2' do
      expect(3 - 1).to eq 2
    end
  end
end

役割によってdescribeを用いてグルーピングする事で可読性を上げることができます。

主要なマッチャ

さて、RSpecでは適切なマッチャを使いこなすことが上達への一歩でもありますが、ここでは使用頻度が高いと思われる主要なマッチャをリスティングしておきます。

machar 使い方 使用目的
to expect(1 + 1).to eq 2 「~であること」を期待する
not_to / to_not expect(1 + 1).not_to eq 3 「~でないこと」を期待する
eq expect(1 + 1).to eq 2 期待値と実際の値が「等しい」ことを期待する
be expect(1 + 1). to be >= 1 等号や不等号と組み合わせて値の大小を検証する
be_xxx(predicate matcher) expect([]).to be_empty ?で終わるRubyのメソッドをbe_xxxの形で検証できる
be_truthy / be_falsey expect(user.save).to be_falsey 真偽値を返すものについてtrue or falseを期待する
change expect{ X }.to change{ Y }.from(A).to(B) X(式)するとYがAからBに変化することを期待する。.by()を使うとその変化の差を検証できる
include expect(x).to incude 1 指定したオブジェクトが含まれていることを期待する
raise_error expect{1 / 0}.to raise_error ZeroDivisionError エラーが出る事を期待する

「be」は等号、不等号を使わずに検証する事も可能です、しかしその場合は「インスタンス」が等しいかどうかを検証してしまうので、同じ値でもインスタンスが異なる場合にテストが失敗してしまいます。基本的には等しいことを検証する場合には「eq」を使っておけば問題ないと思います。

上の主要なマッチャを覚えていれば基本的なテストコードは書けるのでしっかりと覚えていきたい所です。

まとめ

今回はRSpecの基本中の基本をまとめてみました。

基本中の基本となるのはやはりdescribe / it / expect の意味を把握する事、そしてエクスペクテーションを適切なマッチャを利用してテストコードを記述する事です。

RSpecはこれ以上に複雑な構文やマッチャを多数保有しています。まだまだ学習する事は多そうなので、少しずつ勉強していこうと思います。

RSpecをこれから学習するという方も、是非今回の記事で紹介した基本をしっかり頭に叩き込んでから次のフェーズへ移りましょう。

スポンサードリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です