6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2021年9月30日 現場Rails Chapter5 Rspecの基本形

Spec(テスト)の書き方  

Rspecでは、以下の基本フォーマットに従ってSpec(テスト)を書きます。

# テスト対象
describe '〜機能', type: :system do

  # 条件下で想定する動作 
  context '〇〇の場合' do 
  
    before do
      # テストコード実行前に実行する処理(前提条件)
    end
    
    # テストケース
    it '仕様の内容(期待の概要)' do
      # 期待通り、対象が動作するとSpec成功となり、想定通り動作しない場合、予期せぬ例外としてerrorが出ます。
      # また、例外ではなく想定と違う場合にFailureが結果として出ます。
    end
  end
end

Rspecの主要なメソッド  

主要メソッドの詳細を、以下にまとめます。

describe  

「何について仕様を記述しようとしているのか」を記述します。
System Specでは達成したい機能や処理の名前を記述するケースが多いです。

例)
掲示板検索機能、画像アップロード機能、etc

context

テストの内容を「状況・状態」のバリエーションごとに分類するために利用します。技術的にはdescribeとエイリアスとなっていて、テストケースを整理・分類するために用いられます。主に、describe内で使います。

例)
トップ画像を1枚選択してアップロード, トップ画像を2枚選択してアップロード

it  

itはテストケースを表します。itには "期待する動作を説明する文字列" と、"期待する動作" を書きます。

例)
トップ画像が登録されること
本文で絞り込み検索ができること (この場合はcontextは省略)
トップページに〇〇ボタンが表示されている (この場合contextは省略)

  it '〇〇する' do
    # 期待する動作
  end

before  

describeやcontext内にbeforeを記述すると、対応するdescribeやcontextの領域内のテストコードを実行する前に、beforeのブロック内に書かれたコードを実行してくれます。つまり、beforeはその領域全体の前提条件を実現するためのコードを記述する場所です。

本書ではまだ解説されていない内容にはなりますが、以下のように使われます。

  # ユーザーログインを行いユーザー詳細ページにアクセスしておきたいケース
  before do
    login(user)
    visit user_path
  end

同じ条件下で複数のテストケースを実行したい場合、beforeに書くことでDRYにできます。
beforeとitの関係については、現場Rails P.196の説明を引用します。

beforeの処理は、itが実行されるたびに新たに実行されます。次のitが実行されるまでにデータベースの状態は元に戻される為、あるテストケースのせいで別のテストケースが影響を受けるということは基本的には起きないようになっています。

FactoryBotとは?

Railsではテストで利用するデータベースとその他の目的で使うデータベースとを切り離して管理しています。そのため、テストで用いるテストデータを個別で作る必要があるのです。 FactoryBotはテストデータを簡単に作成するためのGemです。FactoryBotを使って、テストデータを作成する流れは以下のようになります。

  1. FactoryBotでデータを作成するためのテンプレートを用意します。
  2. System Specの適切なbefore(事前準備)などで、FactoryBotのテンプレートを使って、テスト用データベースにテストデータを投入します。
  3. 次のテストケースに進む前にデータ状態を戻す処理は、System Specが適切に処理を行ってくれます。

FactoryBotでテストデータを作成出来るように準備する

  • Userのファクトリを作成
# spec/factories/users.rb

FactoryBot.define do
  factory :user do
    name { 'テストユーザ' }
    email { 'test1@example.com' }
    password { 'password' }
  end
end

上記では、factoryというメソッドを利用して、Userクラスのファクトリ(テストデータ)を定義しています。

Userクラスでファクトリ名を異なる名前にする場合はfactoryメソッドに対して:classオプションを指定します。

factory :admin_user, class: User do
 ...(省略)
  • Taskのファクトリを作成
# spec/factories/tasks.rb

FactoryBot.define do
  factory :task do
    name { 'テストを書く' }
    description { 'RSpec & Capybara & FactoryBotを準備する' }
    # userは assosiation :userの省略形です。
    user
<200b>  end
end

先程の例でUserクラス側でファクトリ名を異なる名前にした場合は、下記のように記述します。

assosiasion :user, factory: :admin_user
# Userクラスのadmin_userというファクトリを紐付けます。
# ここでもassosiationを省略してuser factory: :admin_user とも書けます。

traitとは?

traitとは、FactoryBotで複数のサンプルデータを作成する際に使われるテクニックです。Factoryが複数ある場合、traitを使用することで記述の重複を減らすことができ、可読性を高めたコードにすることができます。

FactoryBot.define do
  factory :モデル名 do
    trait :上記モデルを継承したインスタンスの属性値 do
      #モデルの状態の違いによって、作成できるデータの場合分けができる
    end
    trait :上記モデルを継承したインスタンスの属性値 do
  
    end
    
  end
end

以下の例は、userモデルが一般の場合(traitを付けない時)と、:editorの場合と、:writerの場合によって作成されるデータを分けています。 実際に、テストデータを作成する時に、:editorや:writerなどの属性値を渡すことで、traitで設定した適切なデータを作成することが出来ます。

FactoryBot.define do
  factory :user do
    id { '1' }
    name { 'admin' }
    crypted_password { User.encrypt('password') }
    role { :admin }

    trait :editor do
      id { '2' }
      name { :editor }
      crypted_password { User.encrypt('password') }
      role { :editor }
    end

    trait :writer do
      id { '3' }
      name { :writer }
      crypted_password { User.encrypt('password') }
      role { :writer }
    end
  end
end


# writer属性を設定したtraitのテストデータを作成
$ user1 = factory(:user, :writer)`

読み進めていく中でGemfileでgemのバージョンを指定する記号の意味について疑問に思ったので調べました  

バージョンを上げるには3つのバージョンを使って表記します。

バージョンは、x.y.zの形式で表すことが出来ます。

  • X: メジャーバージョン
    大規模な変更が起き、今までの過去のバージョンとの互換性が失われるときにメジャーバージョンの数字をあげます。

  • Y: マイナーバージョン
    過去のバージョンとの互換性は失われませんが、新規機能の開発や機能改善が行われたときにマイナーバージョンの数字をあげます。自分たちのプロジェクトの影響を最小限に抑えつつきちんとアップデートするのであればマイナーバージョンは上げましょう。

  • Z: パッチバージョン
    主にバグ修正のためのバージョン情報です。バグ修正が行われたときにパッチバージョンの数字をあげます。自分たちのプロジェクトで扱う際には必ずこのバージョンは上げたほうがいいです。

Xはメジャーバージョン
yはマイナーバージョン
zはパッチバージョン


# バージョンはBundlerにお任せ
gem 'faker'


# バージョンを1.7.2に固定
gem 'faker', '1.7.2'


# 1.7.2以上(上に制限はなし)
gem 'faker', '>= 1.7.2'


# 1.7.2かつ1.8未満
# バッチバージョンの変更はOK。ただし、マイナーバージョンとメジャーバージョンの変更は行わない
gem 'faker', '~> 1.7.2'


# 1.7以上かつ2.0未満
# バッチバージョンとマイナーバージョンは変更OK。たが、メジャーバージョンは変更は行わない。
gem 'faker', '~> 1.7' 


以上のように、バージョンを指定しないと、互換性が失われる場合があります。例えば、$ bundle updateを使う場合など。(メジャーバージョンが変わってしまうため) 互換性があるとは、古いバージョンから、新しいバージョンにアップデートしても大丈夫であるということです。マイナーバージョンを上げる時は互換性に影響がない場合が多いですが、メジャーバージョンを変えてしまうと互換性がなくなってしまうことが多いので、注意が必要です。

参照

「セマンティック バージョニング」を読んだのでバージョニングについてまとめた

RSpecの(describe/context/example/it)の使い分け

Gemfile の ~> という記号はどういう意味なのか

gem のversion指定 ~>>= の差

【RSpec初級編】Factoryの継承をスマートに記述できるtrait(トレイト)を使いこなそう!

FactoryBotでtraitを使おう

factory_bot

セマンティックバージョニングとは