Redmine 4.0(Rails 5.2)のプラグインをテストするなど(コントローラーのテスト編) - torutkのブログ では、コントローラーのテストにRails 5で雛形生成されるActionDispatch::IntegrationTestを少し追求してみましたが頓挫してしまい、Rails 4までのActionController::TestCaseでのテストを進めることにします。
最初のテスト項目
最初に、indexアクションを呼び応答が成功を返すことをテスト項目とします。
まず、雛形として生成されたTermCategoriesControllerクラスのテストクラスは次です
require File.dirname(__FILE__) + '/../test_helper' class TermCategoriesControllerTest < ActionController::TestCase # Replace this with your real tests. def test_truth assert true end end
試みの1
1つだけ定義されるテストメソッドの名前を、test_index_responseとします。テストメソッドの中でindexアクションをgetで呼び出します。呼び出した結果が成功かどうかをassertで判定します。次のコードとなります。
def test_index_response get :index assert_response :success end
実行結果はエラーとなりました。
Error: TermCategoriesControllerTest#test_index_response: ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"term_categories"} plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:6:in `test_index_response'
term_categories は、プロジェクトにネストしているので、URLパスは /projects/:project_id/term_categories のようになりますが、ここではget呼び出し時にプロジェクトの指定をしていません。そのため、URLパスは /term_categories のようになり、ルーティング設定に合致しないのでエラーとなります。
試みの2
URLパスがプロジェクトのネストとなるように、プロジェクト識別子を追加します。試みの1との差分は次となります。
def test_index_response - get :index + get :index, params: { project_id: 1 } assert_response :success end
実行結果は次のように故障(failure)となりました。
Failure: TermCategoriesControllerTest#test_index_response [/work/redmine/plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:7]: Expected response to be a <2XX: success>, but was a <404: Not Found>
ルーティング設定に合致し、処理が少し進みましたが、リソースが見つからないので404の応答となっています。プロジェクトID 1に対応するプロジェクトが存在していないので(多分)、このような結果となります。
Redmine本体のtest/fixtures/ディレクトリ下には、テストデータが用意されているので、プロジェクトのテストデータを利用することとします。
試みの3
プロジェクトのデータを、fixtures/projects.ymlから読み込むよう指定します。
class TermCategoriesControllerTest < ActionController::TestCase
+ fixtures :projects
def test_index_response
実行結果は次のように故障(failure)となりました。
Failure: TermCategoriesControllerTest#test_index_response [/work/redmine/plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:9]: Expected response to be a <2XX: success>, but was a <403: Forbidden>
今度はリソースへのアクセスが禁止(アクセス権がない)403の応答となっています。
指定したプロジェクトで用語集プラグインが有効化されていないためと推測します。
そこで、プロジェクトで用語集プラグインを有効化させるメソッドを先に呼びます。
試みの4
fixturesで用意されているプロジェクトデータの中から、今回はID=1のデータを読み込み、そのプロジェクトでモジュール(プラグイン)用語集を有効化します。fixuturesにあるデータを参照するには、ID等から検索するか、fixturesのテストデータを取得するメソッドを利用するかの方法があります。
fixtures/projects.yml のテストデータ(ID=1)は次のようになっています(抜粋)。
projects_001: created_on: 2006-07-19 19:13:59 +02:00 name: eCookbook updated_on: 2006-07-19 22:53:01 +02:00 id: 1 :(略)
このテストデータを参照する方法は、モデルの検索で次のように取得するものと、
project1 = Project.find(1)
fixutres参照専用のメソッド
fixtures :projects : project1 = projects('projects_001')
と、fixturesで指定した名称(YAMLファイル名の拡張子を除いた名前)と同名で用意されるメソッドを使うものがあります。
今回は、後者の方法でfixturesにあるプロジェクトデータへの参照を取得します。
def test_index_response projects('projects_001').enabled_module_names = [:glossary] get :index, params: { project_id: 1 } assert_response :success end
プロジェクトのモジュール(プラグイン)を有効化するメソッドenabled_module_namesで用語集プラグインのモジュール名glossaryを指定しています。
実行結果は次のように故障(failure)となりました。
Failure: TermCategoriesControllerTest#test_index_response [/work/redmine/plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:9]: Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://test.host/login?back_url=http%3A%2F%2Ftest.host%2Fprojects%2F1%2Fterm_categories> Response body: <html><body>You are being <a href="http://test.host/login?back_url=http%3A%2F%2Ftest.host%2Fprojects%2F1%2Fterm_categories">redirected</a>.</body></html>
用語集プラグインの権限の設定で参照するにはプロジェクトのメンバーでログインしている必要があるので、未ログインでアクセスするとログインページにリダイレクトされています。
そこで、テストの実行にあたりログインを再現しておきます。
試みの5
リクエストパラメーターにユーザーIDを指定します。
fixtures :projects, :users def test_index_response projects('projects_001').enabled_module_names = [:glossary] @request.session[:user_id] = users('users_002').id get :index, params: { project_id: 1 } assert_response :success end
実行結果は次のように故障(failure)となりました。
Failure: TermCategoriesControllerTest#test_index_response [/work/redmine/plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:10]: Expected response to be a <2XX: success>, but was a <403: Forbidden>
ログイン済みでアクションを呼び出しているので、loginアクションへのリダイレクトが発生することはなくなりましたが、ログイン済みのユーザーのロールが用語集プラグインの権限を有していないので403応答となっています。
そこで、ユーザー(ID=2)のロールに、用語集プラグインの権限を付与します。
試みの6
テストデータのプロジェクト(ID=1)にユーザー(ID=2)が所属しているかをmembersテーブルで確認します。テストデータなのでfixtures/members.ymlを調べると、次のとおりproject_id=1にuser_id=2が所属していることが確認できました。
members_001: created_on: 2006-07-19 19:35:33 +02:00 project_id: 1 id: 1 user_id: 2 mail_notification: true
次に、ユーザー(ID=2)がどのロールでプロジェクト(ID=1)に属しているかを、member_rolesテーブルで確認します。テストデータではfixures/member_roles.ymlを調べると、次の通りmember_id=1はrole_id=1のロールで属していることが分かります。
member_roles_001: id: 1 role_id: 1 member_id: 1
roles_id=1は、fixtures/roles.ymlを調べると、
roles_001: name: Manager id: 1 builtin: 0 :(略)
となっています。
そこで、ロール(ID=1)に用語集プラグインの権限を付与するコートを追加します。
fixtures :projects, :users, :roles def test_index_response projects('projects_001').enabled_module_names = [:glossary] roles('roles_001').add_permission! :manage_term_categories @request.session[:user_id] = users('users_002').id get :index, params: { project_id: 1 } assert_response :success end
実行結果は次のとおり故障(failure)となりました。
Failure: TermCategoriesControllerTest#test_index_response [/work/redmine/plugins/redmine_glossary/test/functional/term_categories_controller_test.rb:11]: Expected response to be a <2XX: success>, but was a <403: Forbidden>
ここでは成功を期待していましたが、どうしてでしょうか。
テストデータの参照で、プロジェクトからロールを辿るのに、先ほど、membersテーブル、member_rolesテーブルを順に手繰ってrolesテーブルに至ると記述しました。テストコードの実行において権限の確認で同様にプロジェクトからロールへ辿るために、fixturesの指定でこの手繰る際に参照する各テーブルのデータを指定しておく必要があるようです。
試みの7
ということでfixturesの指定を追加します。
fixtures :projects, :users, :roles, :members, :member_roles def test_index_response projects('projects_001').enabled_module_names = [:glossary] roles('roles_001').add_permission! :manage_term_categories @request.session[:user_id] = users('users_002').id get :index, params: { project_id: 1 } assert_response :success end
実行結果は、次の通り成功となりました。
... Finished in 0.554461s, 5.4107 runs/s, 5.4107 assertions/s. 3 runs, 3 assertions, 0 failures, 0 errors, 0 skips
setupメソッドに準備設定を記述
さて、最初のテスト項目が成功するようになりました。
次にテスト項目を追加していく際に、すべてのテストメソッドに準備設定としてプロジェクトのモジュール(プラグイン)有効化、ロールに権限の付与、を記述するのは冗長です。
そこで、setupメソッドに準備設定を記述します。
require File.dirname(__FILE__) + '/../test_helper' class TermCategoriesControllerTest < ActionController::TestCase fixtures :projects, :users, :roles, :members, :member_roles def setup @project = projects('projects_001') @project.enabled_module_names = [:glossary] roles('roles_001').add_permission! :manage_term_categories end def test_index_response @request.session[:user_id] = users('users_002').id get :index, params: { project_id: 1 } assert_response :success end end