このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。
https://github.com/hkob/hkob_blog
はじめに
Rails Tips の 57 回目です。index による記事一覧が表示できたので、記事を1件表示する部分をテスト・実装します。
7.1 記事を1件表示する
こちらも先にテストを記述しましょう。object は後で削除に使えるように削除可能なオブジェクトである can_delete fixture を使います。ここではページタイトルにタイトルが表示されるので、その文言が含まれていることを確認します。
let!(:article) { articles :can_delete } let!(:object) { article } #(中略 index のテスト) describe "GET #show" do subject { -> { get article_path(object) } } it_behaves_like "レスポンスコード確認", 200 it_behaves_like "描画結果に文字列が含まれている?", %w[This is the second article] end
この結果、 undefined method
article_path'` というエラーになります。route.rb に index のものしか用意していないからです。ガイドでは show だけの routing を追加していますが、面倒なので 7.2 で紹介される resources を設定してしまいます。これによりテストは文字列が表示されていないというだけになります。
resources :articles
まず、app/controllers/articles_controller.rb
で @article
を取得します。ここでは、objects_from_params 他2件: 小林研 Rails Tips (19)の object_from_params_id
を使います。object_from_params_id
については application_controller.rb
に記載します。
def show @article = object_from_params_id Article end
取得したコンテンツを app/views/articles/show.html.haml
で描画します。
- content_for :title do - @page_title = @article.title %p= @article.body
これでテストが通過しました。それでは、このページへのリンクを index の記事一覧に追加しましょう。ここでは、lotfp ヘルパー : 小林研 Rails Tips (38)で紹介した lotfp を使います。こちらはパスを書くだけでタイトルなどを自動的に設定してくれるものです。
%td= lotfp article_path(article)
lotfp
は本来権限に応じてリンクの可否を判定するのですが、今回は権限周りは実装していないので、権限周りを省略したものを準備します。なお、この中で controller_action_and_params
は controller_and_action ヘルパー : 小林研 Rails Tips (32) に、link_title_from_cap
は link_title_from_path ヘルパー : 小林研 Rails Tips (35) に、その中で呼ばれている title_from_cap
は title_from_path ヘルパー : 小林研 Rails Tips (34) で紹介しているので、そちらから転記します。lotfp
は application_helper.rb
に記載します。元々権限周りがあったので、テストはヘルパ自体の記述していませんでした(権限に関わる部分は request_spec でテストできるため)。
# @param [String] path # @param [Symbol, nil] method # @return [ActiveSupport::SafeBuffer] リンクまたは文字列 # @note lotfp -> link_or_title_from_path def lotfp(path, method = nil, cond: true, title: nil, option: {}) cap = controller_action_and_params(path, method) # 権限周りはスキップ hp = true # controller.acp(privileges_from_cap(cap), cap) title ||= link_title_from_cap(cap) cond && hp ? link_to(title, path, {data: {turbo_method: method}}.merge(option)) : title end
application_helper_spec.rb
のテストは今回の Article に合わせて書き換えてみました。controller_action_and_params
については、ちょうど resources の routes のテストにもなっていますね。
require "rails_helper" describe ApplicationHelper, type: :helper do subject { helper } describe "common test" do it_behaves_like "単一メソッド呼び出し" do let(:article) { articles :can_delete } let(:test_set) do { controller_action_and_params: [ articles_path, {controller: "articles", action: "index"}, article_path(article), {controller: "articles", action: "show", id: article.id.to_s}, new_article_path, {controller: "articles", action: "new"}, [articles_path, :post], {controller: "articles", action: "create"}, edit_article_path(article), {controller: "articles", action: "edit", id: article.id.to_s}, [article_path(article), :patch], {controller: "articles", action: "update", id: article.id.to_s}, [article_path(article), :delete], {controller: "articles", action: "destroy", id: article.id.to_s}, ], controller_and_action: [ articles_path, %w[articles index], article_path(article), %w[articles show], new_article_path, %w[articles new], [articles_path, :post], %w[articles create], edit_article_path(article), %w[articles edit], [article_path(article), :patch], %w[articles update], [article_path(article), :delete], %w[articles destroy], ], key_from_cap: [ [{controller: "articles", action: "index"}, :title], "記事一覧", [{controller: "articles", action: "show"}, :link_title], "表示", ], link_title_from_cap: [ [{controller: "articles", action: "index"}], "記事一覧", [{controller: "articles", action: "show"}], "表示", ], link_title_from_path: [ articles_path, "記事一覧", article_path(article), "表示", ], link_title_with_path: [ articles_path, ["記事一覧", articles_path], article_path(article), ["表示", article_path(article)], ], title_from_cap: [ [{controller: "articles", action: "index"}], "記事一覧", [{controller: "articles", action: "show"}], "記事詳細", ], title_from_path: [ articles_path, "記事一覧", article_path(article), "記事詳細", ], t_ars: [ [Article], "記事", [Article, :title], "タイトル", [Article, :body], "本文", [Article, %i[title body]], %w[タイトル 本文], ] } end end it_behaves_like "単一メソッド呼び出し(キーワード引数あり)" do let(:test_set) do { labels: [ [Article, %i[title body]], {}, "<tr><th>タイトル</th><th>本文</th></tr>", [Article, %i[title]], {add_control: true}, "<tr><th>タイトル</th><th>制御</th></tr>", ], } end end end end
このままだと show の翻訳文字列が設定されていないのでエラーになります。こちらも show のものを追加しました。
ja: articles: index: title: 記事一覧 show: title: 記事詳細 link_title: 表示 common: control: 制御
application_helper.rb に該当するメソッドを追加していくとテストが全て通過しました。request_spec の描画でもテストが成功しています。
ArticlesController GET #index behaves like レスポンスコード確認 レスポンスのステータスが 200 であること behaves like 描画結果に文字列が含まれている? レスポンスの文字列が「["記事一覧", "タイトル"]」を含むこと GET #show behaves like レスポンスコード確認 レスポンスのステータスが 200 であること behaves like 描画結果に文字列が含まれている? レスポンスの文字列が「["This", "is", "the", "second", "article"]」を含むこと
おわりに
今回は show の中身よりも lotfp
を実現するための application_helper
の実装がメインになってしまいました。ただ、このおかげでこの先は lotfp だけでリンク描画ができるので楽になります。