はじめに
- 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を入れれば良い。
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に以下を追加する
--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
をいう記述を削除する。
<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, ~
の記述を追加する。
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になることを確かめるテストコード
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を以下のように編集する
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メソッドをテストするまとまり」であることを示している。このように、describe
とdo
の間にメソッド名を書く際は#
をつけるのが習慣である。
nicknameが空の場合登録できないことを確かめるテストコードを書く
「nicknameが空の場合登録できないことを確かめる」exampleを書いていく。
● 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本体に記述されている。
⑦の行はチェックした結果インスタンスが持つエラー文が期待したものであるか確かめる
⑦の行は、以下のようなコードになっている。
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のバリデーションをかけるのを忘れずに⭐️