モデルのテストを書く
テストを書くのは自分自身で def
したメソッドがあるモデル。理由としては、それ以外の部分については、フレームワークとして信用できるのでわざわざテストする必要はないため。
例えば、次のようなモデルがあるとする。
class User < ApplicationRecord ... def following?(user) active_relationships.where(following_id: user.id).exists? end def follow(user) active_relationships.find_or_create_by!(following_id: user.id) end def unfollow(user) relationship = active_relationships.find_by(following_id: user.id) relationship&.destroy! end ... end
上記のフォロー、アンフォローメソッドのテストする。まずは、モデルテストのスケルトンを作る。
% bin/rails generate test_unit:model モデル名 create test/models/user_test.rb create test/fixtures/users.yml
上記コマンドの引数に、属性名と型(例:title:string
)を与えると、fixtures にテンプレートが用意される。fixtureは、『事前に用意したテストデータを読み込み、常にデータベースの内容を一定に保つための仕組み』のこと。
とりあえずfixtureを使わずにテストを書くので、 test/fixtures/users.yml
は全てコメントアウトしておく。
# test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase test '#follow' do he = User.create!(email: 'he@example.com', password: 'password') her = User.create!(email: 'her@example.com', password: 'password') assert_not he.following?(her) he.follow(her) assert he.following?(her) end test '#unfollow' do he = User.create!(email: 'he@example.com', password: 'password') her = User.create!(email: 'her@example.com', password: 'password') he.follow(her) assert he.following?(her) he.unfollow(her) assert_not he.following?(her) end end
上記はテストメソッド内でデータを生成しているが、fixtureを使うと事前にテストデータを用意できる。
# test/fixtures/users.yml he: email: 'he@example.com' her: email: 'her@example.com'
テストメソッド内でfixtureを使うために、少しテストコードを修正する。
# test/models/user_test.rb class UserTest < ActiveSupport::TestCase fixtures :users test '#follow' do he = users(:he) her = users(:her) assert_not he.following?(her) he.follow(her) assert he.following?(her) end test '#unfollow' do he = users(:he) her = users(:her) he.follow(her) assert he.following?(her) he.unfollow(her) assert_not he.following?(her) end end
テストメソッドに重複が現れたので、setup
メソッド(各テストの実行前に呼ばれるメソッド)でDRYにする。
# test/models/user_test.rb class UserTest < ActiveSupport::TestCase fixtures :users setup do @user = users(:hoge) @he = users(:he) @her = users(:her) end test '#follow' do assert_not @he.following?(@her) @he.follow(@her) assert @he.following?(@her) end test '#unfollow' do @he.follow(@her) assert @he.following?(@her) @he.unfollow(@her) assert_not @he.following?(@her) end end
関連付けを行っている場合のフィクスチャ
関連付けを行っている場合には、2つの異なるフィクスチャの間に参照ノードを定義する。例えば、次のような関連があるモデルに対して、report
モデルのフィクスチャを定義することを考える。
まずは、 user
モデルのフィクスチャを設定。
# test/fixtures/users.yml hoge: email: 'hoge@example.com'
次に関連付けされている report
モデルにフィクスチャを設定する。
# test/fixtures/reports.yml one: user: hoge title: test body: hogehgoe
関連付けされている属性は、 user
モデルで定義したフィクスチャ hoge
を利用する。そうしてあげることで、 one
と hoge
が関連付けされる。
Railsのテストランナー
rails でテストを実行するには、 rails test
を叩けば良い。その際に、オプションを指定することでテスト実行の範囲を指定できる。
コマンド | 内容 |
---|---|
rails test |
全てのテストを一括実行 |
rails test テストファイルのパス |
指定したテストファイル内のテストケースを実行 |
rails test テストファイルのパス -n テストケース名 |
指定したテストファイル内の指定したテストケース名を実行。接頭辞として test_ をつけて空白は _ に置換して渡す。 |
rails test テストファイルのパス:行番号 |
指定したテストファイルの指定した行のテストケースを実行 |
rails test テストファイルのあるディレクトリパス |
指定したディレクトリパス内のテストファイルないのテストケースを実行 |
rails test -h |
テストランナーのヘルプを確認する |
システムテスト
システムテストを実行するときは下記コマンドを叩く。
% rails test:system
システムテストのテンプレートは、下記コマンドで生成できる。
% rails generate system_test reports # => create test/system/reports_test.rb
ちょっと書いてみた
assert_selector を完全一致で使う
例えば、次のように本一覧ページにアクセスできることをテストしたいとする。
<h1>
タグに 『本』が含まれていれば良いと考えて、次のようにテストを書いた。
# test/system/books_test.rb test 'visiting the index' do visit books_url assert_selector 'h1', text: '本' end
ただ、上記だと、本の詳細ページも h1
タグがあり『本』が含まれているので、詳細ページへ間違ってリンクされていたとしてもテストが通ってしまう。
実際に下記のようなテストを実施してみると、テストが通ってしまう💦
# test/system/books_test.rb test 'visiting the index' do # 本一覧ページのテスト visit books_url assert_selector 'h1', text: '本' # 本詳細ページのテスト click_on '新規作成' fill_in 'タイトル', with: 'チェリー本' fill_in 'メモ', with: 'わかりやすい' fill_in '著者', with: 'Junichi Ito' click_on '登録する' assert_selector 'h1', text: '本' end
これは、 assert_selector 'h1', text: '本'
のアサートでは部分一致となっているため。
assert_selector
の説明には、下記のような記述があり、Finders#all
で受け付けられるオプションを使用できる。
It also accepts all options that Finders#all accepts,
Finder#all
を覗いてみると下記の記述がある。
text (String, Regexp) - このテキストを含むか、この正規表現にマッチする要素のみを検索します。
exact_text (String, Regexp, String) - String の場合は、 要素に含まれるテキストが正確に一致する必要があり 、 Boolean の場合は、 テキストオプションが正確に一致する必要があるかどうかを制御します。
完全一致にしてあげるために次のようにしてみる。
test 'visiting the Report index' do visit reports_url # 正規表現で完全一致 assert_selector 'h1', text: /^本$/ # exact_text で完全一致 assert_selector 'h1', exact_text: '日報' end
これで、h1
が意図した値に完全一致するかを指定して検証できるようになった🤗