前提知識
Railsのテストは、Ruby標準のテスト機能であるMinitestをWeb向けに拡張したもので、両方のリファレンスを活用します。
-
Railsテスティングガイド
- Railsのテスト方法に関する網羅的なドキュメント
-
Minitest::Assersions
- Ruby標準のアサーション機能であるMinitestのリファレンス。
- 実行結果の妥当性をチェックする処理をアサーションと言う
- RSpecはRuby標準・Rails標準とは別物であり、組み合わせて使うことはない
-
Rails標準のテスト準備
- 多くのケースでデータベースのセットアップは必要
- データ作成に手間をかけることは非推奨と思われる
事前セットアップ
setup
メソッドで事前処理、teardown
メソッドで事後処理を書けます。
Webアプリケーションでは、状態に応じて挙動を変えるケースが多々あります。以下のようにclassを階層化することで、複数の状態をセットアップできます。
class SomeTest < ActiveSupport::TestCase
class ValidCase < self
setup do
# セットアップA
end
test "OK with valid settings" do
end
end
class InvalidCase < self
setup do
# セットアップB
end
test "NG with invalid settings" do
end
end
end
この例で、ValidCase内に書いたtestブロックは、セットアップAの状態でスタートし、InvalidCase内に書いたtestブロックは、セットアップBの状態でスタートします。
なお、テストケースでDBに加えられた変更はケース終了時にROLLBACKが走るため、手動でteardown
で消す必要はありません。2番目に書いたテストケースが1番目の処理結果に基づくような書き方をするのも間違っています。
インテグレーション・テストの書き方
RailsはWeb特有のインテグレーション・テストの書き方をサポートしています。Webリクエストの中には複数の処理が含まれることが多く、一連のフロー中にエラーがないことを確認します。比較的少ないテストケースでカバレッジが上がりやすく、テスト効率は優れています。
そのためには、ActionDispatch::IntegrationTest
を継承したテストを作成することで、urlにリクエストを送って挙動を確認するテストを実施できます。
書き方の例のコア部分は以下のようになります。
class SomeControllerTest < ActionDispatch::IntegrationTest
test "should get users list" do
get '/users/index'
assert_response :success
assert_select 'table#users'
end
end
get
メソッドにパスを指定してリクエストする- ほかにも
post
やpatch
メソッドなどHTTPリクエストに対応するメソッドがある - 第2引数にリクエストパラメータを指定できる
- HTTPリクエストの詳細なカスタマイズについては、 ActionDispatch::Integration::RequestHelpersのAPIdoc参照
- ほかにも
assert_response
メソッドでHTTPステータスを判定する- リダイレクトを追跡するメソッドもある
assert_redirected_to
を使うと次ステップのpathもチェックできる
assert_select
でViewに存在する特徴的なタグをチェックできる- CSSクエリセレクタと同様の書き方で指定
- メール送信もテスト可能
結合テストはある状態(setupメソッドで再現)とパラメータを用いてリクエストした際の外形的な挙動を観察します。インスタンス変数など値のチェックは実施しません。
ログインセッションを再現する例などの応用については、
IntegrationtestクラスのAPIdocに解説があります。
値のチェックは別途ユニットテストに書きますが、ユニットテストはよくあるテストの書き方とさして違わず、特筆すべき点はありません。
また、結合テストと似たようなテスト手法にシステムテストがあります。これはブラウザの操作をシミュレートするものです。ユーザー操作に最も近いテストではありますが、HTML構造に依存した書き方が必要となるため、やや低レベルのテストコードを書く必要があります。
Write系の確認方法
外形的なテストでWrite系の処理を確認する場合、以下のように1件増えていることのアサーションでチェックすると良いでしょう。
また、POSTリクエストもparams
オプションでモデルのパラメータを送信でき、異常な値のケースや攻撃のシミュレートもできます。
class SomeControllerTest < ActionDispatch::IntegrationTest
test "should create new user" do
assert_difference('User.count', 1) do
post '/users', params: { name: "Julius Caesar" }
end
assert_response :redirect
end
end
同様にdelete
も1件減っていることをチェックすると正常系をテストできます。
メールの確認
ActionMailerで送信するメール処理についても、以下のような方法でテスト可能です。
deliver_now
で送信しているメールについては、assert_difference('ActionMailer::Base.deliveries.size', +1)
で送信件数の増加をカウントすることにより送信挙動を確認できるdeliver_later
で送信しているメールについては、assert_enqueued_emails(1)
でキューをカウントすることで送信挙動を確認できる- 配信したメールは、
ActionMailer::Base.deliveries.last
で取り出せる。subject
やto
の内容を確認する deliver_later
の非同期処理は、perform_enqueued_jobs
を実行した後であれば、同期送信のメールと同様に扱える
テストダブル
スタブなどのテストダブルについては、Minitestで書ける範囲に制約があるため、gemを追加利用した方が無難でしょう。
mochaを利用する書き方を
Rubyのテストにスタブを実装するで解説しています。
外部のAPIを呼び出しているような処理では、テストケースのsetup
でスタブを実装するのが自然でしょう。
インスタンス変数のチェック
インテグーションテストでは変数のテストは非推奨であり、assert_select
でViewのレンダリング結果をテストするなど、より外形的な結果を確認すべきです。
ただし、リクエスト後にControllerオブジェクトを取得できるため、RubyのObject#instance_variable_get
で以下のようにインスタンス変数を取得できます。
@controller.instance_variable_get('@data')
Chuma Takahiro