torutkのブログ

ソフトウェア・エンジニアのブログ

Redmine 4.0(Rails 5.2)のプラグインをテストするなど(コントローラーのテスト編)

Redmine 3.x(Rails 4.2)までは、コントローラーの雛形を生成する際に一緒に生成される機能テスト(functional)のテストコードではActionController::TestCaseを継承していました。

class TermCategoriesControllerTest < ActionController::TestCase

Redmineを離れて、Rails 5ではコントローラーの雛形を生成する際に一緒に生成されるコントローラーのテスト(controller)のテストコードではActionDispatch::IntegrationTestを継承します。

class SomethingsControllerTest < ActionDispatch::IntegrationTest

ただし、Redmine 4.0(開発途上版 trunkブランチ)のコントローラー生成タスクでは従来どおりActionController::TestCaseを継承したテストコードが生成されます。

Redmine 4.xのプラグインにおけるコントローラーのテストで、ActionDispatch::IntegrationTestを使うとどうなるのかを探ってみます。

ActionDispatch::IntegrationTestの場合

コントローラーの一番単純なテストケースは、indexアクションを呼び出しHTTPステータスの成功が返ってくることになります。

ActionController::TestCaseを継承したテストクラスでは、次の様に記述していました。

class TermCategoriesControllerTest < ActionController::TestCase
  def test_index
    get :index, params: { project_id: 1 }
    assert_response :success
  end
end

しかし、ActionDispatch::IntegrationTestを継承したテストクラスでこの記述のままだと実行時にエラーとなります(おかしなURLパスが生成されます)。

Error:
TermCategoriesControllerTest#test_index:
URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80index

getメソッドの第1引数 :index が、ホスト(なぜかhttp://www.example.com:80)に直接付いてしまっています。ActionDispatch::IntegrationTestではURLパス(URLからホスト名:ポート番号までを除いた残り)を指定しなくてはならないようです。

URLパスを指定する方法(1)

ルーティング設定でresources/resourceを指定していると、名前付きルーティングが生成されるので、ヘルパーメソッドで指定が可能です。しかし、今回getで生成しているのでデフォルトでは名前付きルーティングが生成されません。

プラグインのconfig/routes.rbで次のように記述していると、

  get    '/projects/:project_id/term_categories', to: 'term_categories#index'

生成されるルーティングは次のようになり、名前付きルーティングはありません。

          GET  /projects/:project_id/term_categories(.:format)    term_categories#index

プラグインのconfig/routes.rbで次のようにasで名前を指定すると、

  get    '/projects/:project_id/term_categories', to: 'term_categories#index', as: 'project_term_categories'

生成されるルーティングは次のように、名前付きルーティングが設定されます。

project_term_categories  GET  /projects/:project_id/term_categories(.:format)  term_categories#index
class TermCategoriesControllerTest < ActionDispatch::IntegrationTest
  def test_index
    get project_term_categories_url, params: { project_id: 1 }
    assert_response :success
  end
end
URLパスを指定する方法(2)

名前付きルーティングがない場合、URLパス文字列を自前で構成する方法があります。

    get "/projects/#{@project.id}/term_categories", params: { project_id: 1 }
ユーザーの指定方法?

ログインが必要となるプラグイン機能のテストでは、ログイン情報を持たないでテストを実行すると、ログイン画面へのリダイレクトが発生し、テスト結果が成功ではなく302(リダイレクト)となっています。

ActionController::TestCaseでは、@request.session にユーザー情報を入れてログイン状態でのテストを実施できました。

ActionDispatch::IntegrationTestでは、post でログインをしてから対象テストを実施するようです。
ログインのHTTPメソッド(POST)を送ってからコントローラーのアクションに対応するHTTPメソッドを呼ぶのですが、ログイン情報の引継ぎの方法などが分かりません。

このあたりのノウハウが欲しいところです。