第6回 Ruby on Rails〜タスク管理アプリケーション テストの実装②(System Specの実装part1)〜

Ruby

はじめに

前回の続きでRailsの自動テストについて学んでいきます。
前回はRailsの自動テストについて学んだ後自動テストを実装する準備をしました。
今回からは実際にタスク管理アプリにシステムテストを実装していきます。

Rspecの書き方

Rspecでは以下のようなフォーマットに従ってSpecを記述します。

describe [仕様を記述する対象(テスト対象)], tyoe: [Specの種類] do

 context [ある状況・状態] do
  before do
   [事前準備]
  end

  it [仕様の内容(機体の概要)] do
    [期待する動作]
  end
 end
end

itに到達するまでに、describeやcontextを何個もネストすることもできます。
describe, context, itを並べて複数記述することもできます。

Railsのテストで登場するこれらの用語は大きく2つのタイプに分けることができます。

  • テストケースの整理・分類・・・describe, context
  • テストコードの実行・・・brefore, it

describe

describeには「何について記述しようとしているのか」(テストの対象)を記述します。

通常、一番外側のdescribeにはそのSpecファイル全体の主題を記述します。内部にdescribeをネストすることもあり、階層の深いdescribeにはより細かいテーマを記述します。describeのブロック内(do〜end)にテスト本体を記述します。

context

contextは、テストの内容を「状況・状態」の条件ごとに分類するために利用します。System Specでは、ユーザーの入力内容の正誤、ログインの有無などの様々な条件をcontextに記述し、続くブロック内に、それらの条件下で想定する動作を記述します。

before

beforeをdescribeやcontext内に記述すると、describeやcontextの領域内のテストコードを実行する前に、beforeに書かれたコードが実行されます。

it

itには、期待する動作を文章とブロック内のコードで記述します。it内に書いた期待する動作通りに対象が動作すれば成功となります。想定通りに動作しない場合は、Specが予期せぬ例外が出ればエラー(Error)、そうでないけれども想定と違う場合には失敗(Failure)として結果がカウントされます。

breforeの処理はitが実行されるたびに実行されます。次のitが実行されるまでにデータベースの状態は元に戻されます。そのためテストケースが別のテストケースの影響を受けることは基本的にありません。

Feature Specではdescribeの代わりにfeature、itの代わりにscenarioと記述することができます。System Specでも、feature/scenarioを使って記述できます。

テストデータ作成の準備

Railsではテスト用のデータベースとその他の目的で使うデータベースとは切り離しています。テストではテスト用のデータベースにに対し、テストの前提となるデータをテストケース実行前に投入し、次のテストケース実施前までに元に戻します。
System SpecでFactoryBotを利用してテストデータを投入する場合には、次のようなステップを踏みます。

  1. Factory Botでデータを作成する「テンプレート」を用意する
  2. System Specのbeforeなどで、FactoryBotのテンプレートを利用してテスト用データベースにテストデータを投入する

次のテストケースの実施前にデータ状態を戻す処理は、System Specが適切に処理します。

ファクトリの定義

それでは、タスク管理アプリにSystem Specで利用するためのファクトリを定義していきます。

タスク管理アプリには2つのモデルがあります。アプリへのログインの動作を確認するにはデータベースにUserデータが登録されている必要があります。タスク一覧でタスクが正常に表示されることを確認するにはデータベースにTaskデータが登録されている必要があります。そこでUserとTaskについてファクトリを用意します。

まずはUserのファクトリを追加します。以下の内容でspec/factories/user.rbを作成します。

spec/factories/user.rb

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

factoryメソッドを利用して、:userという名前のUserクラスのファクトリを定義しています。ファクトリ名とクラス名が異なる場合には:classオプションを記述してクラスを指定する必要があります。

factory :admin_user, class: User do
・・・

これで名前が「テストユーザー」、メールアドレスが「test1@example.com」、パスワードが「password」のUserテストデータを定義できました。

続いてTaskについてのファクトリをつくります。以下の内容でspec/factories/tasks.rbを作成します。

spec/factories/tasks.rb

FactoryBot.define do
  factory :task do
    name {'テストを書く'}
    description {'Rspec & Capybara & FactoryBot を準備する'}
    user
  end
end

「user」を記述することで:userという名前のFactoryをTaskモデルに定義されたuserという名前の関連を生成するのに利用できます。関連名とファクトリ名が異なる場合はuserの代わりに以下のように記述します。

association :user, factory: :admin_user

このように記述すると、:taskというファクトリを利用してTaskオブジェクトを生成する際に、同時に:userというファクトリを利用して作られたUserオブジェクトがuser関連に入った状態を作成できます。

以上でタスク管理アプリでSystem Specを書くために必要なテストデータのファクトリを準備できました。

タスク一覧機能のSystem Spec

まずは一覧画面に遷移したら作成済みのタスクが表示されるというSpecを書きます。

Specの大枠

Specの大枠は以下のようになります。

spec/system/tasks_spec.rb

require 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      
    end
    context 'ユーザーAがログインしている時' do
      before do
        
      end

      it 'ユーザーAが作成したタスクが表示される' do
        
      end
    end
  end
end

「ユーザーAが作成したタスクが表示される」というテストケースについては以下の順番でコードが実行されます。

  • 最初のbefore
    • テストデータの準備(ユーザーA、作成者がユーザーAのタスク)
  • 2つ目のbefore
    • ユーザーAでブラウザからログインする。
  • it
    • ユーザーAが作成したタスクの名称が画面上に表示されていることを確認

ユーザーAの作成

一覧表示機能の前処理ではログインやタスクの用意に必要となるユーザーデータをデータベースに登録します。
FactoryBot.createメソッドで:userファクトリを指定することでユーザーデータを登録できます。

spec/system/tasks_spec.rbの初めのbeforeの下にユーザーAを作成するコードを追記します。

spec/system/tasks_spec.rb

require 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
    end
    context 'ユーザーAがログインしている時' do
      before do
        
      end

      it 'ユーザーAが作成したタスクが表示される' do
        
      end
    end
  end
end

属性値を指定して渡すことで一部属性を変更してデータを作成できます。

作成者がユーザーAのタスクの作成

一覧画面を表示したときに表示されて欲しいタスクデータを用意します。

先ほどと同様にFactoryBot.createを利用します。

spec/system/tasks_spec.rb

equire 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
      FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    end
    context 'ユーザーAがログインしている時' do
      before do
        
      end

      it 'ユーザーAが作成したタスクが表示される' do
        
      end
    end
  end
end

ここまでで必要なUserとTaskのテストデータが準備できました。最初のbeforeの中身は完成です。

ユーザーAのログイン

ブラウザ上でユーザーAがログインする操作を以下のステップに沿って記述します。

  1. ログイン画面にアクセスする
  2. メールアドレスを入力する
  3. パスワードを入力する
  4. 「ログインする」ボタンを押す

ログイン画面へのアクセス

ログイン画面へのアクセスはvisit[URL]を用いて2つ目のbefore下に記述します。

spec/system/tasks_spec.rb

before do
  visit login_path
end

メールアドレスの入力

メールアドレスは「メールアドレス」というラベルがついたテキストフィールドにメールアドレスを入力する操作です。テキストフィールドに値を入れるには、fill_inをいうメソッドを利用します。

spec/system/tasks_spec.rb

before do
  visit login_path
  fill_in 'メールアドレス', with: 'a@example.com'
end

パスワードの入力

メールアドレス同様、fill_inを利用して記述します。

spec/system/tasks_spec.rb

before do
  visit login_path
  fill_in 'メールアドレス', with: 'a@example.com'
  fill_in 'パスワード', with: 'password'
end

「ログインする」ボタンを押す

ボタンを押すにはclink_buttonメソッドを用いて以下のように記述します。

spec/system/tasks_spec.rb

before do
  visit login_path
  fill_in 'メールアドレス', with: 'a@example.com'
  fill_in 'パスワード', with: 'password'
  clink_button 'ログインする'
end

以上でユーザーAがログインする操作を記述できました。2つ目のbeforeの中身は完成です。

タスク名の表示確認

itの中身を記述して、タスク一覧画面にユーザーAが作成者のタスクが表示されることを確認します。it内を以下のように編集します。

spec/system/tasks_spec.rb

it 'ユーザーAが作成したタスクが表示される' do
  expect(page).to have_content '最初のタスク'
end

have_contentの部分はRSpecではマッチャ(Matcher)と呼ばれます。

以上で画面内に特定の内容が存在するかを検査できるようになりました。

最終的なコードは以下のようになります。

spec/system/tasks_spec.rb

require 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
      FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    end
    context 'ユーザーAがログインしている時' do
      before do
        visit login_path
        fill_in 'メールアドレス', with: 'a@example.com'
        fill_in 'パスワード', with: 'password'
        clink_button 'ログインする'
      end

      it 'ユーザーAが作成したタスクが表示される' do
        expect(page).to have_content '最初のタスク'
      end
    end
  end
end

以下のコマンドでSpecを実行してみます。

$ bundle exec rspec spec/system/tasks_spec.rb
Finished in 7.98 seconds (files took 2.94 seconds to load)
1 example, 0 failures

「1 example, 0 failures」と表示されたらテスト成功です。

万が一以下のようなエラーが出る場合は、Gemfileでrails_specのバージョンを上げたほうがいいかもしれません。

Failure/Error:
       raise WrongScopeError,
             "`#{name}` is not available from within an example (e.g. an " \
             "`it` block) or from constructs that run in the scope of an " \
             "example (e.g. `before`, `let`, etc). It is only available " \
             "on an example group (e.g. a `describe` or `context` block)."
     
       `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).

他ユーザーのタスクが表示されないことの確認

タスク管理アプリケーションではタスク一覧画面にて自身が作成したタスクしか表示されないようになっています。

ユーザーBでログインした場合ユーザーAが作成したタスクが表示されないことを確かめるため、一覧表示機能のテストケースを増やします。

Specの大枠

ユーザーBに対して、ユーザーAの作成したタスクが見えないことを以下の手順で確認します。

  1. ユーザーAとユーザーBのタスクを作成する
  2. ユーザーBを作成する
  3. ユーザーBでログインする
  4. ユーザーAのタスクが表示されないことを確認する

大枠は以下のようになります。

spec/system/tasks_spec.rb

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
      FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    end
    
    context 'ユーザーAがログインしている時' do
      ・・・
    end

    context 'ユーザーBがログインしているとき' do
      before do
        
      end

      it 'ユーザーAが作成したタスクが表示されない' do
        
      end
  end
end

一つのdescribe内に複数のcontextを複数記述した場合contextの内容が呼び出される前に実行されます。

ユーザーBのログイン

「ユーザーBがログインしているとき」のcontextの中の処理を書きます。

まずはbefore内を以下のように編集します。

spec/system/tasks_spec.rb

before do
  FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com')
  visit login_path
  fill_in 'メールアドレス', with: 'b@example.com'
  fill_in 'パスワード', with: 'password'
  click_button 'ログインする'
end

続いてit内を記述します。タスクが表示されていないことを期待する場合はhave_no_contentというマッチャを利用します。

spec/system/tasks_spec.rbを以下のように編集します。

spec/system/tasks_spec.rb

require 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
      user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
      FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    end

    context 'ユーザーAがログインしている時' do
      before do
        visit login_path
        fill_in 'メールアドレス', with: 'a@example.com'
        fill_in 'パスワード', with: 'password'
        click_button 'ログインする'
      end

      it 'ユーザーAが作成したタスクが表示される' do
        expect(page).to have_content '最初のタスク'
      end
    end

    context 'ユーザーBがログインしているとき' do
      before do
        FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com')
        visit login_path
        fill_in 'メールアドレス', with: 'b@example.com'
        fill_in 'パスワード', with: 'password'
        click_button 'ログインする'
      end

      it 'ユーザーAが作成したタスクが表示されない' do
        expect(page).to have_no_content '最初のタスク'
      end
    end
  end
end

以下のコマンドでテストを実行します。

$bundle exec rspec spec/system/tasks_spec.rb
..

Finished in 8.41 seconds (files took 3.3 seconds to load)
2 examples, 0 failures

無事成功しました。今回はここまでです。

終わりに

今回はタスク管理アプリケーションにタスク一覧表示機能のSystem Specを実装しました。
次回は引き続き他の機能のSystem Specを実装していきます。

コメント

タイトルとURLをコピーしました