PicTweetのモデルの単体テストコードを書こう

保存できる値は最大6文字まで」というバリデーションを設置します。
user.rbを開き、以下のように記述してください。

app/models/user.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :validatable
  has_many :tweets
  has_many :comments  # commentsテーブルとのアソシエーション

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

これまでは、ユーザー新規登録フォームに最大6文字までしか入力できない制限をかけることで、擬似的なバリデーションとしていました。
しかし、新たにバリデーションを設置したため、フォームの制限は不要になりました。

 

ユーザー新規登録画面のビューファイルを開いてください。
nicknameを入力するフォームのtext_fieldタグに付いている、maxlength: 6を削除しましょう。

app/views/devise/registrations/new.html.erb
1
2
3
4
5
6
7
8
9
<h2>Sign up</h2>
<%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
  <%= devise_error_messages! %>
  <div class="field">
    <%= f.label :nickname %> <em>(6 characters maximum)</em><br>
    <%= f.text_field :nickname, autofocus: true %>
  </div>

<%# 以下省略 %>

以上で事前準備は完了です。

 

exampleを整理しよう

モデル単体テストはバリデーションやメソッドの検証です。

PicTweetでは、Userモデルにメソッドを定義していません。
したがって、バリデーションの挙動のみ検証します。

Userモデルのバリデーションを確認しましょう。

app/models/user.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :comments
  validates :nickname, presence: true, length: { maximum: 6 }
end

バリデーションは「属性の値がDBに保存されてもよいかチェックを行う条件」を設定するものです。


presenceは、指定した属性の値の有無をチェックします。
Userモデルでは、nicknameに対して、presence: trueの条件を指定しています。

この場合、DBに保存する前にnicknameに値が存在していなければ、条件に引っかかりDBに保存されません。

lengthは、指定した属性の値の文字数制限をチェックします。
Userモデルでは、nicknameに対して、length: { maximum: 6 }の条件を指定しています。

この場合、DBに保存する前にnicknameの値が7文字以上であれば、条件に引っかかりDBに保存されません。


このようなバリデーションから、以下のような挙動を検証すべきことがわかります。

  • nicknameが空では新規登録できない
  • nicknameが7文字以上では登録できない
  • nicknameが6文字以下なら登録できる



さらに、Userモデルには9行目の記述で設定したものとは別に、deviseによるバリデーションも自動的に設けられています。
5行目の:validatableという記述がその役割を担っています。

自動的に設けられるバリデーションは以下のとおりです。

  email password
バリデーション ・存在すること
・一意であること
・@を含むこと
・存在すること
・6文字以上128文字以下であること

:validatableで設定される条件より、以下のような挙動を検証すべきことがわかります。

  • nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる
  • nicknameが6文字以下であれば登録できる
  • passwordとpassword_confirmationが6文字以上であれば登録できる
  • nicknameが空では登録できない
  • emailが空では登録できない
  • passwordが空では登録できない
  • passwordが存在してもpassword_confirmationが空では登録できない
  • nicknameが7文字以上では登録できない
  • 重複したemailが存在すれば登録できない
  • passwordが5文字以下では登録できない

以下のように記述してください。

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end

  describe 'ユーザー新規登録' do
    it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる' do
    end
    it 'nicknameが6文字以下であれば登録できる' do
    end
    it 'passwordとpassword_confirmationが6文字以上であれば登録できる' do
    end
    it 'nicknameが空では登録できない' do
    end
    it 'emailが空では登録できない' do
    end
    it 'passwordが空では登録できない' do
    end
    it 'passwordが存在してもpassword_confirmationが空では登録できない' do
    end
    it 'nicknameが7文字以上では登録できない' do
    end
    it '重複したemailが存在する場合登録できない' do
    end
    it 'passwordが5文字以下では登録できない' do
    end
  end
end

 

1. 検証したいモデルのインスタンスを生成する
今回はUserのモデル単体テストであるため、Userモデルのインスタンスを生成しましょう。

2章では、exampleごとにUser.newと記述して、インスタンスを生成していました。
しかし3章以降は、exampleごとにFactoryBotがインスタンスを生成しています。

そのため、itのブロックの中に、インスタンスを生成する記述を加える必要はありません。

具体的には、user_spec.rbの4,5,6行目の記述でインスタンスを生成しています。

2. 生成したインスタンスがどのような状況であればいいかを記述する
エクスペクテーションを記述します。
正常系テストのエクスペクテーションには、be_validマッチャを用います。

be_validビー バリッド

be_validとは、valid?メソッドの返り値が、trueであることを期待するマッチャです。
expectの引数に指定されたインスタンスが、バリデーションでエラーにならないものであれば、valid?の返り値はtrueとなりテストは成功します。

それでは、be_validを用いて、正常系のexampleごとにテストコードを記述してみましょう。

be_validマッチャは、valid?メソッドの返り値がtrueであることを期待するマッチャでした。
FactoryBotによって生成されるインスタンスが、バリデーションでエラーにならないか確かめてみましょう。

ターミナル
1
2
3
4
5
6
7
% rails c
[1] pry(main)> @user = FactoryBot.build(:user)
  (4.3ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<User id: nil, email: "judie@gmail.com", created_at: nil, updated_at: nil, nickname: "GP">
[2] pry(main)> @user.valid?
  User Exists? (10.3ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'judie@gmail.com' LIMIT 1
=> true

上記のように、エラーにならないことが確認できたらexitを入力して抜けましょう。
したがって、@userexpectの引数に指定し、be_validマッチャを用いてエクスペクテーションを記述しましょう。

 

コンソールで確かめた内容を、ファイルに記述します。
以下のように記述してください。

spec/models/user_spec.rb
1
2
3
it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる' do
  expect(@user).to be_valid
end

Userモデルのnicknameに対するバリデーションは、「最大6文字まで」とするものです。
そのため、nicknameに指定された値が、制限範囲内であれば登録できることを期待します。
@userのnicknameを6文字以内で上書きし、@userexpectの引数に指定した上で、be_validマッチャを用いてテストコードを記述しましょう。

spec/models/user_spec.rb
1
2
3
4
it 'nicknameが6文字以下であれば登録できる' do
  @user.nickname = 'aaaaaa'
  expect(@user).to be_valid
end

 

バリデーションでエラーにならない最小値でテストできるように、passwordpassword_confirmationを上書きし、@userexpectの引数に指定した上で、be_validマッチャを用いてテストコードを記述しましょう。

spec/models/user_spec.rb
1
2
3
4
5
it 'passwordとpassword_confirmationが6文字以上であれば登録できる' do
  @user.password = '000000'
  @user.password_confirmation = '000000'
  expect(@user).to be_valid
end

 重複したemailが存在する場合登録できない」についてのテストコード

spec/models/user_spec.rb
1
2
3
4
5
6
7
it '重複したemailが存在する場合登録できない' do
  @user.save
  another_user = FactoryBot.build(:user)
  another_user.email = @user.email
  another_user.valid?
  expect(another_user.errors.full_messages).to include('Email has already been taken')
end

生成した@userをテーブルに保存した後に、再度別のユーザーanother_userを生成します。
そして、another_useremailに、すでに保存済みの@useremailを上書きしています。

その上でanother_userが保存されるかどうかを判別しています。

エラーメッセージはEmail has already been takenです。

 

describeのグループの中でcontextメソッドを用い、新規登録の可否によってさらに細かくグループ分けしましょう。

contextコンテキスト

contextは、特定の条件を指定してグループを分けます。
使用方法はdescribeと同じですが、describeには何についてのテストなのかを指定するのに対し、contextには特定の条件を指定します。

 

contextを用いて条件を指定し、テストコードを2つのグループに分けましょう。
以下のように記述してください。

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end

  describe 'ユーザー新規登録' do
    context '新規登録できるとき' do
      it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できる' do
      end
      it 'nicknameが6文字以下であれば登録できる' do
      end
      it 'passwordとpassword_confirmationが6文字以上であれば登録できる' do
      end
    end
    context '新規登録できないとき' do
      it 'nicknameが空では登録できない' do
      end
      it 'emailが空では登録できない' do
      end
      it 'passwordが空では登録できない' do
      end
      it 'passwordが存在してもpassword_confirmationが空では登録できない' do
      end
      it 'nicknameが7文字以上では登録できない' do
      end
      it '重複したemailが存在する場合登録できない' do
      end
      it 'passwordが5文字以下では登録できない' do
      end
    end
  end
end

 

テストコードを記述するファイルを作成しましょう

以下のコマンドを実行してください。

ターミナル
1
% rails g rspec:model tweet

以下のように、テストコードを記述するためのファイルtweet_spec.rbと、FactoryBotを記述するためのファイルtweets.rbが生成されます。

tweets.rbに、以下のように記述してください。

spec/factories/tweets.rb
1
2
3
4
5
6
7
FactoryBot.define do
  factory :tweet do
    text {Faker::Lorem.sentence}
    image {Faker::Lorem.sentence}
    association :user 
  end
end

5行目にassociation :userという記述があります。
これはusers.rbのFactoryBotとアソシエーションがあることを意味しています。

Tweetモデルのバリデーションは、以下のようになっています。

app/models/tweet.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Tweet < ApplicationRecord
  validates :text, presence: true
  belongs_to :user
  has_many :comments

  def self.search(search)
    if search!=""
      Tweet.where('text LIKE(?)', "%#{search}%")
    else
      Tweet.all
    end
  end
end

textpresenceのバリデーションが設置されています。
さらに、アソシエーションを示すbelongs_to :userには、「TweetはUserに属している必要がある」制約が含まれています。

まとめると、以下のようなexampleを列挙できます。

  • 画像とテキストがあれば投稿できる
  • テキストがあれば投稿できる
  • テキストが空では投稿できない
  • ユーザーが紐付いていなければ投稿できない

これらのexampleをテストコードに落とし込みましょう。

テストコードを記述しましょう

tweet_spec.rbを以下のように編集しましょう。

spec/models/tweet_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'rails_helper'

RSpec.describe Tweet, type: :model do
  before do
    @tweet = FactoryBot.build(:tweet)
  end

  describe 'ツイートの保存' do
    context 'ツイートが投稿できる場合' do
      it '画像とテキストがあれば投稿できる' do
      end
      it 'テキストがあれば投稿できる' do
      end
    end
    context 'ツイートが投稿できない場合' do
      it 'テキストが空では投稿できない' do
      end     
      it 'ユーザーが紐付いていなければ投稿できない' do
      end
    end
  end
end

 

spec/models/tweet_spec.rb
1
2
3
4
5
it 'ユーザーが紐付いていなければ投稿できない' do
  @tweet.user = nil
  @tweet.valid?
  expect(@tweet.errors.full_messages).to include('User must exist')
end

生成した@tweetに紐づくユーザーを、nilとして無いものとしています。その上で、バリデーションを確認しています。エラーメッセージはUser must existです。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

効率的にテストコードを書こう

FactoryBotファクトリーボット

インスタンスをまとめることができるGemです。他のファイルであらかじめ各クラスのインスタンスに定める値を設定しておき、各テストコードで使用します。

 

Gemfileを以下のように編集します。この時も、group :development, :test doの中に記述するように注意してください。

Gemfile
1
2
3
4
5
6
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'
end

ターミナル
1
% bundle install

 

 FactoryBotの記述を格納するディレクトfactoriesと、Userモデルに対するFactoryBotのファイルusers.rbを、以下のように手動で作成しましょう。

 

users.rbを以下のように編集しましょう。

spec/factories/users.rb
1
2
3
4
5
6
7
8
FactoryBot.define do
  factory :user do
    nickname              {'test'}
    email                 {'test@example'}
    password              {'000000'}
    password_confirmation {password}
  end
end

さて、この設定したインスタンスを生成するためには、FactoryBot.build(:user)という記述をテストコードの中に記述します。

 

beforeビフォー

それぞれのテストコードを実行する前に、セットアップを行うことができます。

 

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
require 'rails_helper'
RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end

  describe 'ユーザー新規登録' do
    it 'nicknameが空では登録できない' do
      @user.nickname = ''
      @user.valid?
      expect(@user.errors.full_messages).to include "Nickname can't be blank"
    end
    it 'emailが空では登録できない' do
      @user.email = ''
      @user.valid?
      expect(@user.errors.full_messages).to include "Email can't be blank"
    end
  end
end

 

Fakerフェイカ

ランダムな値を生成するGemです。メールアドレス、人名、パスワードなど、さまざまな意図に応じたランダムな値を生成してくれます。

 

Gemfileに以下の様に追記します。今回はFakerをテストコードのために使用するので、これまで同様group :development, :test doの中に記述しましょう。

Gemfile
1
2
3
4
5
6
7
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'
  gem 'faker'
end
ターミナル
1
% bundle install

FactoryBotの記述を編集しましょう

以下のようにusers.rbを編集しましょう。

spec/factories/users.rb
1
2
3
4
5
6
7
8
FactoryBot.define do
  factory :user do
    nickname              {Faker::Name.initials(number: 2)}
    email                 {Faker::Internet.free_email}
    password              {Faker::Internet.password(min_length: 6)}
    password_confirmation {password}
  end
end

ここまで編集できたら、テストコードを実行して、変わらず成功することを確かめましょう。

テストコードを実行しましょう

以下のコマンドを実行しましょう。

ターミナル
1
% bundle exec rspec spec/models/user_spec.rb 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

RSpecの導入と単体テストコードを書く練習

rails_helperレイルズ ヘルパー

Rspecを用いてRailsの機能をテストするときに、共通の設定を書いておくファイルです。各テスト用ファイルでspec/rails_helper.rbを読み込むことで、共通の設定やメソッドを適用します。

rails gコマンドでテストファイルを生成すると、rails_helperを読み込む記述が、自動的追加されます。

 

モデル単体テストの練習

 

describeディスクライブ

describeとは、テストコードのグループ分けを行うメソッドです。
「どの機能に対してのテストを行うか」をdescribeでグループ分けし、その中に各テストコードを記述します。

describeにつづくdo~endの間に、さらにdescribeメソッドを記述することで、入れ子構造をとることもできます。

describeメソッドを用いて、「ユーザー新規登録」についてのテストを行うことを記述しましょう。

 

spec/models/user_spec.rb
1
2
3
4
5
6
7
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    # ユーザー新規登録についてのテストコードを記述します  
  end
end

上記のように記述することで、「どの機能に対してのテスト行うか」を明記できました。

 

itイット

itメソッドは、describeメソッド同様に、グループ分けを行うメソッドです。
itの場合はより詳細に、「describeメソッドに記述した機能において、どのような状況のテストを行うか」を明記します。

また、itメソッドで分けたグループを、exampleとも呼びます

 

require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    it 'nicknameが空では登録できない' do
      # nicknameが空では登録できないテストコードを記述します
    end
    it 'emailが空では登録できない' do
      # emailが空では登録できないテストコードを記述します
    end
  end
end

itを用いて、「nicknameが空では登録できない」と「emailが空では登録できない」の2つのexample(テストコードのグループ)を作成しました。

 

bundle execバンドル エグゼクコマンド

Gemの依存関係を整理してくれるコマンドです。

RSpecをはじめ、多くのGemはその他のGemと関係があり、互いに依存しています。
したがって、bundle execコマンドを用いてGemの依存関係を整理する必要があるのです。

応用的な内容であるため、「bundle execコマンドが必要になることがある」と認識しておきましょう。

 

rspecアールスペック コマンド

specディレクトリ以下に書かれたRSpecのテストコードを実行するコマンドです。
実行するファイルを指定することも可能です。

 

異常系のモデル単体テストの実装は、以下の流れで進みます。

  1. 保存するデータ(インスタンス)を作成する
  2. 作成したデータ(インスタンス)が、保存されるかどうかを確認する
  3. 保存されない場合、生成されるエラーメッセージが想定されるものかどうかを確認する

nicknameとemailのテスト実装を通して、この流れを理解しましょう。

 

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
require 'rails_helper'
RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    it 'nicknameが空だと登録できない' do
      user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
    end
    it 'emailが空では登録できない' do
    end
  end
end

valid?バリッド

valid?は、バリデーションを実行させて、エラーがあるかどうかを判断するメソッドです。
エラーがない場合はtrueを、ある場合はfalseを返します。
また、エラーがあると判断された場合は、エラーの内容を示すエラーメッセージを生成します。

 

expectationエクスペクテーション

エクスペクテーションとは、検証で得られた挙動が想定通りなのかを確認する構文のことです。
expect().to matcher()を雛形に、テストの内容に応じて引数やmatcherを変えて記述します。

 

matcherマッチャ

matcherは、「expectの引数」と「想定した挙動」が一致しているかどうかを判断します。
expectの引数には検証で得られた実際の挙動を指定し、マッチャには、どのような挙動を想定しているかを記述します。

 

includeインクルード

includeは、「expectの引数」に「includeの引数」が含まれていることを確認するマッチャです。

具体的には、以下のように記述します。

【例】includeマッチャのテストコード
1
2
3
4
5
describe 'フルーツ盛り合わせ' do
  it 'フルーツ盛り合わせにメロンが含まれている' do
     expect(['りんご', 'バナナ', 'ぶどう', 'メロン']).to include('メロン')
  end
end

このように記述することで、りんごバナナぶどうメロンが入った配列に、メロンが含まれることを想定しています。

このテストコードを実行した場合、想定通り配列のなかにメロンは含まれているため、テストは成功します。

eqイーキュー

eqは、「expectの引数」と「eqの引数」が等しいことを確認するマッチャです。

具体的には、以下のように記述します。

【例】eqマッチャのテストコード
1
2
3
4
5
describe '加算' do
  it '1 + 1の計算結果は2と等しい' do
     expect(1 + 1).to eq(2)
  end
end

このように記述することで、1 + 1という計算の結果が、2と等しいことを想定しています。

このテストコードを実行した場合、想定通り1 + 12と等しいため、テストは成功します。

 

errorsエラーズ

errorsは、インスタンスにエラーを示す情報がある場合、その内容を返すメソッドです。

 

full_messagesフル メッセージー

full_messagesは、エラーの内容から、エラーメッセージを配列として取り出すメソッドです。

ターミナルでuser.errors.full_messagesと実行すると、以下のようにエラーの内容の配列が返されます。

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
require 'rails_helper'
RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    it 'nicknameが空では登録できない' do
      user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
      user.valid?
      expect(user.errors.full_messages).to include("Nickname can't be blank")
    end
    it 'emailが空では登録できない' do
    end
  end
end

 

% bundle exec rspec spec/models/user_spec.rb 

 

<emailが空の場合の記述をしよう>

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    it 'nicknameが空では登録できない' do
      user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
      user.valid?
      expect(user.errors.full_messages).to include("Nickname can't be blank")
    end
    it 'emailが空では登録できない' do
      user = User.new(nickname: 'test', email: '', password: '000000', password_confirmation: '000000')
      user.valid?
      binding.pry
    end
  end
end

 

expectにはエラーメッセージの配列を、includeには想定しているエラーメッセージを記述します。
以下のように記述してください。

spec/models/user_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
    it 'nicknameが空では登録できない' do
      user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
      user.valid?
      expect(user.errors.full_messages).to include("Nickname can't be blank")
    end
    it 'emailが空では登録できない' do
      user = User.new(nickname: 'test', email: '', password: '000000', password_confirmation: '000000')
      user.valid?
      expect(user.errors.full_messages).to include("Email can't be blank")
    end
  end
end

 

ターミナル
1
% bundle exec rspec spec/models/user_spec.rb 

 

expect(X).to eq Yという構文を用いて記述します。Xには今回テストする値、Yには期待する結果が入ります。

expect(X).to eq Yという構文を以下の表で整理しましょう。

X Y
2 + 3 == 10 false

上記の通り、今回はX(2+3 == 10)はYであること(falseであること)を期待します。expect(2 + 3 == 10).to eq falseと下記のように追記します。

spec/sample_spec.rb
1
2
3
4
5
6
7
RSpec.describe '簡単なテストコード' do
 describe '数字の計算' do
   it '2+3が10になるのは誤りである' do
     expect(2 + 3 == 10).to eq false
   end
 end
end

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

RSpec

Rspecアールスペック

RSpec(アールスペック)は、Ruby on Railsのテストコードを書くために用いられるGemです。

  • テストコードを書いて実行することで、自動でWebアプリケーションの挙動確認ができる
  • Ruby on RailsのテストコードはRSpecを用いて書くことができる

 

単体テストコード

モデルやコントローラーなどの機能ごとに問題がないか確かめます。たとえば、PicTweetにおけるTweetモデルのテストコードでは「画像URLとテキストがない投稿は、テーブルに保存させない」というバリデーションの挙動を確認します。

結合テストコード

ユーザーがブラウザで操作する一連の流れを再現して、問題がないか確かめます。たとえば、PicTweetにおけるツイート投稿のテストコードでは「画像URLとテキストを投稿して送信ボタンを押すと、投稿完了ページに移動し、TOPページに戻るとさきほど投稿した内容が表示されている」という流れを一気に確かめます。

Git Github

リポジトリ

リポジトリとは、Gitの管理下にあるファイルやディレクトリの変更履歴を保管しておく箱のようなものです。管理したいアプリケーションのディレクトリを、バージョン管理の範囲として指定します。

 

ローカルリポジトリ

ローカルリポジトリとは、自分のPC上(ローカル環境)に置くリポジトリのことです。
作成したリポジトリは自分のパソコンの中にあるため、ファイルやディレクトリを変更、修正した際は好きなタイミングでこれを記録できます。

 

リモートリポジトリ

リモートリポジトリとは、外部サーバー上に置くリポジトリのことです。作成した箱が、インターネットを介した別の場所にも作られるイメージです。リモートリポジトリを直接変更修正することはなく、ローカルリポジトリの変更修正を同期して、反映させることで更新します。

 

commitコミット

コミットと読みます。ファイルやディレクトリの変更修正を、リポジトリに記録することです。
commitをすることで、変更修正の時系列の管理が行いやすくなり、変更修正を遡ることもできます。

commitの際は、「どのような変更修正なのか」をわかりやすくするためにメモを添えます。それをコミットメッセージと言います。

 

ブランチ

ブランチとは、リポジトリで管理しているファイルやディレクトリの変更の流れ、すなわちcommitの連なりです。リポジトリは必ずブランチを持っています。

このブランチは分岐ができます。本流を「masterブランチ」、分岐したブランチを「トピックブランチ」と呼びます。

ブランチのメリットを以下にまとめました。

  • リポジトリで管理しているファイルやディレクトリの本流に影響を与えずに作業ができること
  • ブランチを切ることで目的別に同時並行で作業が行えること
  • 不具合が発生した場合も対応が容易になること

プルリクエス

ブランチでのコミット履歴を残すと共に、各コミットにおける変更修正にコメントをつけることができるGitHubの機能のことを言います。以下の動画のような形で、1つのブランチでの作業について、コードを確認しつつコミュニケーションが取れる掲示板のようなものがプルリクエストです。

 

mergeマージ

マージと読みます。mergeは統合するという意味です。機能実装のために作成したブランチを、リモートリポジトリ上のmasterブランチに反映する作業のことです。

 

pullプル

プルと読みます。リモートリポジトリの変更をローカルリポジトリに取り込む操作のことを言います。

リモートリポジトリのmasterブランチはマージ後の情報になっています。しかし、ローカルリポジトリのmasterブランチはマージ前の情報のままなので、次のブランチを作成する前にpullをして情報を反映させる必要があります。

 

git cloneギット クローンコマンド

リモートリポジトリを自分のパソコンにダウンロードするコマンドです。
以下のようにリモートリポジトリのURLに.gitを加えた文字列指定することで、ローカルリポジトリとして取り込むことができます。ダウンロードをするようなイメージです。

【例】ターミナル
1
% git clone (ダウンロードしたいリモートリポジトリのURL)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Rails 設計

NOT NULLノット ヌル制約

NOT NULL制約は、テーブルの属性値にNULL(空の値)が入らないように制限する制約です。

たとえば、usersテーブルの「name」カラムにNOT NULL制約を設定すると、nameカラムが空のレコードだと保存できなくなります。この制約は、t.string :nameにnull: falseと記述することで、設定できます。

【例】db/migrate/2020XXXXXXXX_create_users.rb
1
2
3
4
5
6
7
8
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.timestamps null: false
    end
  end
end

指定したカラムが、DBに空のままの状態で保存するのを防ぎます。

 

【例】
1
t. :カラム名, null: false

 

一意性制約

一意性制約は、テーブル内で重複するデータを禁止する制約です。
emailカラムに対して一意性制約を設定すると、同じemailのレコードは保存できなくなります。

外部キー制約

外部キー制約とは、外部キーの対応するデータが必ず存在しなくてはいけないという制約です。

たとえば、PicTweetで学習した、tweetsテーブルのuser_idというカラムには、この外部キー制約をつけるべきです。usersテーブルのidが主キーであり、そのidで判別できるレコードと関連付けを行う場合に使用します。

この制約は、t.integer :user_idにforeign_key: trueと記述することで、設定できます。

【例】db/migrate/2020XXXXXXXX_create_tweets.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class CreateTweets < ActiveRecord::Migration[6.0]
  def change
    create_table :tweets do |t|
      t.string :name
      t.string :text
      t.text :image
      t.integer :user_id, foreign_key: true
      t.timestamps
    end
  end
end

関連性のあるテーブル同士を結びつける場合に、対応する外部キーのカラム名の後に記述します。
foreign_key: trueを設定する理由は、外部キーに該当するカラムのデータが空の場合、異なるレコードとの関連付けが行えなくなるからです。

書き方は、以下のように記述します。

【例】
1
t. :カラム名,  foreign_key: true
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Railsのエラーを解消しよう

デバッグ

デバッグとは、任意でない処理(バグ)を見つけることです。

Railsで行うデバッグ作業の1つに、pry-railsというGemの機能を利用する方法があります。

 pry-railsプライ レイルズ

Railsにおけるデバッグ用のGemです。
すでに、PicTweetのプロジェクト作成時に導入しています。

pry-railsデバッグツールに属し、作業の際にバグの有無を確認したり、処理を止めてソースコードが正しいかを確認できるものです。

デバッグツールを使いこなすことで、より開発がしやすくなります。

binding.pry(バインディングプライ)という機能を使ってみましょう。

binding.pryは、pry-railsを追加すると扱えるようになる機能です。
binding.pryという記述をソースコードの中にすると、binding.pryが存在する箇所でRailsの処理を一時停止し、その状態でコンソールを起動できます。

 

Gemfile
1
2
3
4
5
~省略~

gem 'pry-rails'

~省略~

 

createアクション内にbinding.pryを記述しよう

app/controllers/tweets_controller.rb
1
2
3
4
5
6
7
8
~省略~

  def create
    binding.pry
    Tweet.create(tweet_params)
  end

~省略~