🗂

Minitestで テストを書く

2021/06/20に公開

初めに

現場で使える Ruby on Rails 5速習実践ガイド では、開発現場で広く使われていることから、RSpecによってテストが書かれています。
しかしながら、初めて学ぶ方にとっては、RSpecは些か難しく感じるのではないでしょうか。

ここでは、Ruby on Rails の標準テストフレームワークであり、簡潔にテストを書ける Minitest を試してみます。

現場で使える Ruby on Rails 5速習実践ガイド

準備

Gemfile に minitest を追加。
minitest-reporters で、テスト結果を見やすくします。

# Gemfile
group :test do
  gem 'capybara'                     # 仮想的に利用者のブラウザ操作を行う
  gem 'selenium-webdriver'
  gem 'webdrivers'

  gem 'minitest'                     # Rails標準のテスト用フレームワーク
  gem 'minitest-reporters'           # テスト結果を見やすく表示
end

minitest-reporters で、テスト結果を見やすくするとともに、
利用者のログイン、ログアウトのテストを行えるよう、login_helperを追加します。

# test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require_relative 'login_helper'
require 'rails/test_help'
require 'minitest/reporters'
Minitest::Reporters.use!

class ActiveSupport::TestCase
  # parallelize(workers: :number_of_processors)
  parallelize(workers: 1) # エラーが出るようならこちら
  fixtures :all
end
# test/login_helper.rb
module LoginHelper
  def login_as(user)
    visit login_url
    fill_in 'メールアドレス', with: user.email
    fill_in 'パスワード', with: 'password'
    click_on 'ログインする'

    @current_user = user
  end

  def current_user
    @current_user
  end
end

fixture(フィクスチャ) を用意します。
フィクスチャとは、英語で造り付けの什器の意味です。
Railsは、このフィクスチャをテスト用のデータベースに自動的に読み込みます。
サンプルデータを作成するためには、Factory-bot等、より高機能なGemもありますが、ここでは、設定不要で簡潔に分かり易く導入できるfixtureを使います。

# test/fixtures/users.yml
# 一般の利用者 taro
taro:
  name: taro
  email: taro@example.com
  password_digest: <%=BCrypt::Password.create('password', cost: BCrypt::Engine::MIN_COST)%>
  admin: true

# 管理者 admin
admin:
  name: admin
  email: admin@example.com
  password_digest: <%=BCrypt::Password.create('password', cost: BCrypt::Engine::MIN_COST)%>
  admin: true

# 十人の利用者を作ることもできます。
<% 1.upto(10) do |i| %>
user_<%= i %>:
  name: user_<%= i %>
  email: user_<%= i %>@example.com
  password_digest: <%=BCrypt::Password.create('password', cost: BCrypt::Engine::MIN_COST)%>
  admin: false
<% end %>

fixtureは、user: taro と書くことで、関連付けを設定することもできます。

# test/fixtures/tasks.yml
taro_task:
  name: 太郎のタスク
  description: タスクの説明いろいろ
  user: taro

システムテスト

システムテストは、ウェブアプリを実際に利用者がブラウザで操作した場合のテストを行います。
少し実行に時間がかかることもありますが、利用者の操作を模倣でき、また失敗した際にはスクリーンショットも自動で取られるので、便利です。

テストの作成

Rails には、システムテストを容易に作成できるようジェネレータが実装されています。

% rails g system_test welcomes

このコマンドを実行することで、システムテストの雛型が作成されますので、少し手を加えます。

# test/system/welcomes_test.rb
require "application_system_test_case"

class WelcomesTest < ApplicationSystemTestCase
  test "トップページに Taskleaf と表示されている" do
    visit root_url
    assert_selector "h1", text: "Taskleaf"
  end
end

テストの実行

以下のコマンドで、全てのシステムテストが実行できます。

% rails test:system

一部のテストを実行するには、次のコマンドを使います。

% rails test test/system/welcomes_test.rb

システムテストが失敗したようです。
スクリーンショット 0002-12-13 22.57.23.jpg

失敗すると、自動的にスクリーンショットが取られます。

/Users/mirai/rails_projects/
taskleaf/tmp/screenshots/
failures_test_トップページに_Taskleaf_と表示されている.png ```
と表示されていますので、Finderで確認できます。

スクリーンショットをもとに、エラーを修正してもう一度実行します。

![スクリーンショット 0002-12-13 23.03.20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96314/df26ef14-494f-2168-739c-8d03b333149b.jpeg)

無事にシステムテストが通りました。

### 利用者のテスト

``` ruby
# test/system/users_test.rb
require "application_system_test_case"

class UsersTest < ApplicationSystemTestCase
  setup do
    @user = users(:taro)
  end

  test "パスワードが正しいと、ログインできる" do
    visit login_url
    fill_in 'メールアドレス', with: @user.email
    fill_in 'パスワード', with: 'password'
    click_on 'ログインする'

    assert_selector ".ui.success.message", text: "ログインしました"
  end

  test "パスワードが違うと、ログインできない" do
    visit login_url
    fill_in 'メールアドレス', with: @user.email
    fill_in 'パスワード', with: 'wrong password'
    click_on 'ログインする'

    assert_selector ".ui.error.message", text: "メールアドレス または パスワードが異なります"
  end
end

タスクのテスト

setup ブロックで事前準備すると、その後のテストでずっと使えるので便利です。

# test/system/tasks_test.rb
require "application_system_test_case"

class TasksTest < ApplicationSystemTestCase
  setup do
    @taro       = users(:taro)
    @jiro       = users(:jiro)

    @taro_task1 = tasks(:taro_task1)
    @taro_task2 = tasks(:taro_task2)
    @taro_task3 = tasks(:taro_task3)

    @jiro_task1 = tasks(:jiro_task1)
  end

  test "太郎でログインすると、太郎が作成したタスクが一覧表示される" do
    login_as(@taro)
    visit tasks_path
    assert_text @taro_task1.name
    assert_text @taro_task2.name
    assert_text @taro_task3.name
  end

  test "太郎でログインすると、太郎が作成したタスクが詳細表示される" do
    login_as(@taro)
    visit task_path(@taro_task1)
    assert_text @taro_task1.name
    assert_text @taro_task1.description
  end

  test "次郎でログインすると、太郎が作成したタスクが表示されない" do
    login_as(@jiro)
    visit tasks_path
    assert_no_text @taro_task1.name
  end

  test "新規作成画面で名前を入力すると、タスクが登録できる" do
    login_as(@taro)
    visit new_task_path
    fill_in '名前', with: '新しい太郎のタスク'
    click_on '登録する'

    assert_selector '.ui.success.message', text: '新しい太郎のタスク'
    within 'table' do
      assert_text '新しい太郎のタスク'
    end
  end

  test "新規作成画面で名前がないと、エラーが表示される" do
    login_as(@taro)
    visit new_task_path
    fill_in '名前', with: '   '
    click_on '登録する'

    within '#error_explanation' do
      assert_text '名前を入力してください'
    end
  end

  test "自分のタスクを編輯できる" do
    login_as(@taro)
    visit tasks_path
    click_on '編輯', match: :first

    fill_in '名前', with: '編輯した太郎のタスク'
    click_on '更新する'

    assert_selector '.ui.success.message', text: '編輯した太郎のタスク'
    within 'table' do
      assert_text '編輯した太郎のタスク'
    end
  end

  test "自分のタスクは削除できる" do
    login_as(@taro)
    assert_equal 3, current_user.tasks.count
    visit tasks_path
    click_on '削除', match: :first
    accept_confirm
    assert_selector '.ui.success.message', text: '削除しました'
    assert_equal 2, current_user.tasks.count
  end
end

メールのテスト

メールの送信テストをすることもできます。

# test/mailers/task_mailer_test.rb
require 'test_helper'

class TaskMailerTest < ActionMailer::TestCase
  # 事前準備
  setup do
    @user = users(:taro)
    @task = @user.tasks.create(name: '本日のタスク',
                               description: 'Ruby on Railsの学習をする')
    @mail = TaskMailer.creation_email(@task)
  end

  test "タスク作成完了メールが送信されること" do
    # メール送信されるかをテスト
    assert_emails 1 do
      @mail.deliver_now
    end

    # 期待通りのメールを送信したかをテスト
    assert_equal "タスク作成完了メール", @mail.subject
    assert_equal [@user.email], @mail.to
    assert_equal ["taskleaf@example.com"], @mail.from
    assert_match "本日のタスク", @mail.html_part.body.to_s
    assert_match "Ruby on Railsの学習をする", @mail.html_part.body.to_s
    assert_match "本日のタスク", @mail.text_part.body.to_s
    assert_match "Ruby on Railsの学習をする", @mail.text_part.body.to_s
  end
end

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

% rails test test/mailers

最後に

簡潔に書けるminitestのご紹介、いかがでしたでしょうか?
皆様のお役に立てば幸いです。

参考

Rails テスティングガイド

Discussion