2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails RSpecの準備とテストコード基礎

Last updated at Posted at 2019-10-27

はじめに

  • RSpecを利用した基礎的なテストコードを書けるようになるために準備をすすめる。

RSpecの準備

まずはRailsに 「rspec-rails」というGemをインストールし、RSpecを利用できるようにする。次にRSpecの設定をし、その後、実際にRSpecのコードを書いてRSpecの基礎文法を学習したいと思う。

Gemのインストール

はじめにRSpecを利用するためのGem 「rspec-rails」をインストールする。

● テキストエディタで、作成したアプリのディレクトリを開く

RailsアプリのGemfileの下部を以下のように編集する

'gem 'rspec-rails'をいう部分を追記する。
またweb_consoleというgemはtest環境で動かすと不具合が起きる可能性があるgemなのでdevelopment環境でのみ動くようにする。
新しく group :development do ~ endというブロックを作成し、その間に移動させる。
※ group :development do ~ endの記述が既にある場合はその記述の間にweb_consoleを入れれば良い。

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'pry-rails'
  gem 'compass-rails'
  gem 'sprockets'
  gem 'kaminari'
  gem 'devise'
  gem 'rspec-rails'
end

group :development do
  gem 'web-console'
end

Gemfileを編集したら、 bundle installをする。

RSpecの設定をする

● ターミナルから以下のコマンドを実行する

ターミナル
$ rails g rspec:install
# RSpec用設定ファイルの作成

下記のようにファイルが作成されれば成功

ターミナル
  create  .rspec
  create  spec
  create  spec/spec_helper.rb
  create  spec/rails_helper.rb

上記作業によって作成された2つのファイルについての説明をしておくと、、、

rails_helper.rb

RailsにおいてRSpecを利用する際に、共通の設定を書いておくファイルである。各テスト用ファイルでこのファイルを読み込むことで、共通の設定や、メソッドを適用する。

spec_helper.rb

rails_helper.rbと同じくRSpec用の共通の設定を書いておくファイルだが、このファイルはRSpecをRails無しで利用する際に利用する。

.rspecに以下を追加する

.spec
--format documentation

以上で準備は完了である。

RSpecが正常に利用できるか確かめてみる

ターミナルから以下のコマンドを実行する

ターミナル
$ bundle exec rspec

以下のような結果になれば、RSpecを利用する準備はできている。

ターミナル
No examples found.

Finished in 0.00031 seconds (files took 0.19956 seconds to load)
0 examples, 0 failures

アプリの元のコードを編集する

テストコードを書く際に、バリデーションが正常に動くようにコードを修正する。

app/views/devise/registrations/new.html.erbを以下のように編集する

nicknameを入力するホームを出力しているtext_fieldタグに付いているmaxlength: 6をいう記述を削除する。

devise/registrations/new.html.erb
  <h2>Sign up</h2>
  <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
    <%= devise_error_messages! %>

    <div class="field">
      <%= f.label :nickname %> <em>(6 characters maximum)</em><br>
      <%= f.text_field :nickname, autofocus: true %>
    </div>
 # 省略

続いてuser.rbを編集する。

app/models/user.rbを以下のように編集する

Userクラス内に、validates :nickname, ~の記述を追加する。

user.rb
  class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :tweets

  validates :nickname, presence: true, length: { maximum: 6 }

end

これで準備は完了。

モデルクラスのテストコードを書く

まずはじめに、Railsにおいて基礎となる単体テストであるモデルのバリデーションに関するテストコードを書く。
「userモデル」が存在するアプリに対応するテストコードを書いていく。

specファイルの命名規則

__specファイルは対応するクラス名_spec.rbという名前になる。__今回はまず「user.rb」に関するspecファイルを作成するので、その場合の名前は「user_spec.rb」になる。

テストコードの基本

「1 + 1が2になることを確かめる」という簡単なテストコードを例として、以下に挙げる基本的なテストコードの文法について書いてみる。

【例】 1 + 1が2になることを確かめるテストコード

sample_spec.rb
describe "hogehoge" do
  it "1 + 1は2になること" do
    expect(1 + 1).to eq 2
  end
end

上記のコードの概要を説明する。
1行目のdescribeというキーワードでテストをグループ化する。
続いて、テスト1つ(example)として評価されるit do ~ endのブロックの中に、expect(x).to eq Yという形式の式を書いていく。これが、実際にテストが成功するかどうかチェックされる式(エクスペクテーション)になる。
以下に挙げるポイントについて説明。

  • describe
  • itとexample
  • エクスペクテーション
  • expectメソッド
  • マッチャ

● describe

describeは、直後のdo ~ endまでのテストのまとまりを作る。describeの後に続く""の中にはそのexampleの説明を書く。

● itとexample

2行目のitは__example__と呼ばれる実際に動作するテストコードのまとまりを表す。itの後に続く""の中にはそのexampleの説明を書く。

● エクスペクテーション

実際に評価される式のことである。it do ~ endの間に書く。上記の式ではexpect(1 + 1).to eq 2の部分がエクスペクテーションである。

● expect(x).to eq Y

エクスペクテーションの文法である。xの部分に入れた式の値がYの部分の値と等しければ、テストが成功する。eqの部分を、マッチャと言う。

● マッチャ

エクスペクテーションの中で、テストが成功する条件を示す。例にあげるとeqは「等しければ」という意味になる。他にもinclude(含んで入れば)、valid(バリデーションされれば)など複数のマッチャが存在する。

試しにバリデーションのテストを書いてみる

rails アプリのディレクトリに、spec/modelsディレクトリを作成する

spec/models/user_spec.rbを作成する。

user_spec.rbを以下のように編集する

user_spec.rb
require 'rails_helper'
describe User do
  describe '#create' do
    it "is invalid without a nickname" do
    end
  end
end

it ~ doの間はそのexampleの説明を書くが、ここは日本語でも問題ない。
この状態でテストを実行する。テストを実行するには、ターミナルでbundle exec rspecというコマンドを実行する。
現状、user_spec.rbには中身のないテストコードが書かれている状態である。

● bundle exec rspecコマンド

RSpecのテストコードを利用したテストを実行するためのコマンドである。

ターミナルから、テストを実行する

ターミナル
$ bundle exec rspec

ターミナルに以下のように表示されれば、テストを正常に実行できている。

ターミナル
User
  #create
    is invalid without a nickname

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

この段階ではまだテストとして評価される式を書いていないので、テストは無条件でパスする。

1行目のrequire 'rails_helper'は、rails_helper.rb内の記述を読み込むことで共通の設定を有効にしている。この1行目の記述は、全てのspecファイルに書き込む。3,4行目に連続してdescribeが書いてあるが、describeは、ネスト(入れ子状)にすることができる。ここでは「userクラスにあるcreateメソッドをテストするまとまり」であることを示している。このように、describedoの間にメソッド名を書く際は#をつけるのが習慣である。

nicknameが空の場合登録できないことを確かめるテストコードを書く

「nicknameが空の場合登録できないことを確かめる」exampleを書いていく。

● user_spec.rbを以下のように編集する

user_spec.rb
require 'rails_helper'
describe User do
  describe '#create' do
    it "is invalid without a nickname" do
     user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000")   # ⑤
     user.valid?  # ⑥
     expect(user.errors[:nickname]).to include("can't be blank")  # ⑦ 
    end
  end
end

上記のコードで追記した⑤,⑥,⑦の行の流れは以下である。

⑤の行でテストしたいプロパティを持ったuserクラスのインスタンスを新規作成する
スペックファイルの中では、そのRailsプロジェクトで作成しているモデルクラスを利用することができる。今回は「nicknameが空である場合登録できないこと」を確かめるテストコードを作成したいのでnicknameの値を空にし、それ以外は適当な値をセットした状態でuserクラスのインスタンスを作成している。

⑥の行で作成したインスタンスがバリデーションによって保存できない状態がチェックする
新規作成したuserクラスのインスタンスがバリデーションに引っかかるかどうかを確かめるvalid?メソッドを利用する。

● valid?メソッド

  • valid?メソッドを利用すると、ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存できない状態であるか」を確かめることができる。

● errorsメソッド

  • valid?メソッドの返り値はtrue/falseだが、valid?メソッドを利用したインスタンスに対してerrorsメソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができる。

ターミナルから以下の指示の通りコマンドを実行する

ターミナル
#コンソールを立ち上げる
$ rails c
#nicknameの値が空であるuserクラスのインスタンスを作成する
>user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000")
#valid?メソッドを利用する
>user.valid?
#errorsメソッドを利用する
>user.errors
=> #<ActiveModel::Errors:0x007ffa6ce07ef0
 @base=
  #<User:0x007ffa6d3430b8
#中略
 @messages={:nickname=>["can't be blank"]}>

下部の@message=~に注目する。この部分が、valid?によって追加された、そのインスタンスが保存できない理由である。上記の場合、「nicknameカラムに値が入っていなければ保存できない」と言われていることになる。

※ここで記述されている"can't be blank"は、元々Rails Gemで用意されているエラーメッセージである。このエラーメッセージは、RailsのGem本体に記述されている。

⑦の行はチェックした結果インスタンスが持つエラー文が期待したものであるか確かめる

⑦の行は、以下のようなコードになっている。

user_spec.rb
expect(user.errors[:nickname]).to include("can't be blank")

● includeマッチャ

includeマッチャは、引数にとった値がexpectの引数である配列に含まれているかをチェックすることができるマッチャである。

今回の場合、「nicknameが空の場合は__can't be blank__というエラーが出るはずだ」ということがわかっているため、include("can't be blank")のように書くことができる。
実際にその通りになればこちらのエクスペクテーションはパスし、このコードは意図した動作をすると保証できる。

ターミナルから以下のようにテストを実行する

ターミナル
$ bundle exec rspec

以下のような結果になれば成功である。

ターミナル
$ bundle exec rspec

User
  #create
    is invalid without a nickname

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

以上を参考にメールアドレスが存在しなければ登録できないことを確かめるテストコードを書いてみて欲しいです。
user.rbにまずはemailのバリデーションをかけるのを忘れずに⭐️

2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?