RSpec学習メモ(everyday Rails RSpecによるRailsテスト入門 第4章)

<アプリケーションにファクトリを追加する>

$ bundle exec rails g factory_bot model:user

# specディレクトリ内にfactoriesという新しいディレクトリが作られる。そしてその中にusers.rbという名前のファイルが作成される。

 

<spec/factories/users.rb>

FactoryBot.define do

  factory :user do

    first_name "Aaron"
    last_name "Sumner"
    email "tester@example.com"
    password "dottle-nouveau-pavilion-tights-furze"

  end

end

 

こうすると、テスト内でFactoryBot.create(:user)と書くことで新しいユーザーを作成することができる。

FactoryBot.create(:user)を使ってテストデータを作れば、そのユーザーは毎回基本的に上記で設定したデータのものとなる。

 

<spec/models/user_spec.rb>

describe User do
  # 有効なファクトリを持つこと

  it "has a valid factory" do

    expect(FactoryBot.build(:user)).to be_valid

  end

end

 

FactoryBotを使用することでこのように簡潔に書き換えることができる。

ここではFactoryBot.buildを使用したので、新しいユーザーはインスタンス化されるだけで、保存はされない。

 

ポイント

FactoryBot.buildを使うと新しいテストオブジェクトをメモリ内に保存する。FactoryBot.createを使うとアプリケーションのテスト用データベースにオブジェクトを永続化する。

 

---------------------------------------------------------------------------------------

 

<シーケンスを使ってユニークなデータを作成する>

FactoryBotではシーケンスを使って、ユニークバリデーションを持つフィールドを扱うことができる。

 

<spec/factories/users.rb>

FactoryBot.define do

  factory :user do

    first_name "Aaron"

    last_name  "Sumner"

    sequence(:email) { |n| "tester#{n}@example.com" }

    password "dottle-nouveau-pavilion-tights-furze"

  end

end

 

新しいユーザーを作成するたびに、、tester1@example.com tester2@example.com というように、ユニークで連続したメールアドレスが設定される。

 

---------------------------------------------------------------------------------------

 

<ファクトリ関連を扱う>

FactoryBotは他のモデルと関連を持つモデルを扱うのにとても便利である。

 

<spec/factories/users.rb>

FactoryBot.define do
  factory :user, aliases: [:owner] do

    first_name "Aaron"
    last_name "Sumner"
    sequence(:email) { |n| "tester#{n}@example.com" }

    password "dottle-nouveau-pavilion-tights-furze"

  end

end

 

<spec/factories/notes.rb>

FactoryBot.define do

  factory :note do

    message "My important note."

    association :project

    user { project.owner }

  end

end

 

<spec/factories/projects.rb>

FactoryBot.define do

  factory :project do

    sequence(:name) { |n| "Project #{n}" }

    description "A test project."
    due_on 1.week.from_now
    association :owner

  end

end

 

Noteはプロジェクトとユーザーの両方に属している。しかし、テストのたびに毎回プロジェクトとユーザーを手作業で作成したくはない。今作りたいのはメモだけである。

 

ポイント

FactoryBotを使用する際は、ユーザーファクトリに対してownerという名前で参照される場合があると伝えなくてはならない。そのためにaliasを使用する。

 

<app/models/project.rb>

class Project < ApplicationRecord

  validates :name, presence: true, uniqueness: { scope: :user_id }

 

  belongs_to :owner, class_name: User, foreign_key: :user_id

  has_many :tasks

  has_many :notes

end

 

---------------------------------------------------------------------------------------

 

<ファクトリ内の重複をなくす>

FactoryBotでは同じ型を作成するファクトリを複数定義することもできる。

特定のファクトリを使って作成するインスタンスのクラス名と、既存のファクトリと異なるインスタンスの属性値を指定する。そしてその作成したインスタンスの名前を引数に渡すことができる。

 

重複を減らすテクニックが二つある。

①ファクトリの継承を使ってユニークな属性だけを変えること。

②トレイと(trait)を使ってテストデータを構築すること。

 

---------------------------------------------------------------------------------------

 

<ファクトリを安全に使用する>

ファクトリを使用するとテスト中に予期しないデータが作成されたり、無駄にテストが遅くなったりする原因となる。

可能な限りFactoryBot.createではなくFactoryBot.buildを使用することで、テストデータベースにデータを追加する回数が減るので、パフォーマンス面のコストを削減することができる。

RSpec学習メモ(everyday Rails RSpecによるRailsテスト入門 第3章)

<モデルスペックの構造>

モデルスペックには次のようなテストを含まれるようにする。

 

• 有効な属性で初期化された場合は、モデルの状態が有効(valid)になっていること。
• バリデーションを失敗させるデータであれば、モデルの状態が有効になっていないこと。

クラスメソッドとインスタンスメソッドが期待通りに動作すること。

 

---------------------------------------------------------------------------------------

 

<モデルスペックの基本構成>

describe User do
  # 姓、名、メール、パスワードがあれば有効な状態であること

  it "is valid with a first name, last name, email, and password" # 名がなければ無効な状態であること
  it "is invalid without a first name"
  # 姓がなければ無効な状態であること
  it "is invalid without a last name"
  # メールアドレスがなければ無効な状態であること
  it "is invalid without an email address"
  # 重複したメールアドレスなら無効な状態であること
  it "is invalid with a duplicate email address"
  # ユーザーのフルネームを文字列として返すこと
  it "returns a user's full name as a string"

end

 

ポイント

・期待する結果をまとめて記述(discribe)している。

・example(itで始まる行)一つにつき、結果を一つだけ期待している。

・どのexampleも明示的である。

・各 exampleの説明は動詞で始まっている。shouldではない。

 

これらを念頭に置きながらモデルのスペックを作成していく。

 

$ bundle exec rails g spec:model user

# spec/models/user_spec.rb が作成される。

# 必ずしもスペックファイルを作成するためにジェネレーターを利用する必要はないが、ジェネレーターを使用することはタイプミスによるエラーを防止するための良い方法である。

 

<spec/models/user_spec.rb>

require ‘rails_helper’

# この記述でRSpecに対し、ファイル内のテストを実行するためにRailsアプリケーションの読み込みが必要であることを伝えている。

 

RSpec.describe User, type: :model do

# describeメソッドを使って、Userという名前のモデルのテストをここに書くことを明示している。

 

---------------------------------------------------------------------------------------

 

<バリデーションをテストする>

新しく作ったユーザー(first_nameには明示的にnilをセットする)に対して、valid?メソッドを呼び出すと有効(valid)にならず、ユーザーのfirst_name属性にエラーメッセージがついていることを期待(expect)する。

 

ポイント

テストが失敗するところを確認することも重要。誤判定ではないことを確認するためには二つの方法がある。

 

①toをto_notに変えてエクスペーションを反転させる。(not_toでも可)

②アプリケーション側のコードを変更して、テストの実行結果にどんな変化が起きるのかを確認する。

 

---------------------------------------------------------------------------------------

 

<describe, context, before, afterを使用してスペックをDRYにする>

・describeではクラスやシステムの機能に関するアウトラインを記述し、contextでは特定の状態に関するアウトラインを記述する。

RSpecのbeforeフックはスペック内の冗長なコードを認識し、きれいにするための言い出発点となる。

before(:each)describeまたはcontextブロック内の各テストの前に実行される。

 

---------------------------------------------------------------------------------------

 

<まとめ>

・期待する結果は能動系で明示的に記述すること。

・起きて欲しいことと、起きて欲しくないことをテストすること。

・境界値をテストすること。

・可読性を上げるためにスペックを整理するが大事だが、テストの場合はDRYであることよりも読みやすいことの方が重要。(DRYすぎてもダメ)