[小ネタ] JbuilderでMissing Template Error

2014.05.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Ruby on RailsにてJbuilderを使用してJSONを返却するControllerを開発中、RSpecからControllerを呼び出した際に以下のエラーに遭遇しました。

ActionView::MissingTemplate: Missing template ・・・

ご存知の人にとっては簡単な話だとは思いますが、意外にはまったので、備忘録として載せておきます。

原因と検証

結論からいうと、原因はControllerを呼び出す際にフォーマットとしてJSONを指定していなかったことです。

検証のためのソースを書いたので、動きを確認してみたいと思います。

Controller

class TopController < ApplicationController
  def index
  end
end
[/ruby]
<p>
検証用なので、Controllerは何もせず、以下のViewフォーマット(今回はJSON)を返却するだけです。
</p>
<h4>Jbuilder</h4>

<p>
こちらも検証のため、固定値をJSON形式で返すだけにしています。
</p>
<h4>RSpec</h4>
<p>
先ずはエラーを起こしたときのソースを書きたいと思います。
</p>

require 'spec_helper'

describe TopController do
  render_views

  describe "GET 'index'" do
    it 'get json' do
      get 'index'
      response.should be_success
      body = response.body
      json = JSON.parse(body)
      expect(json['title']).to eq("Top#index")
      expect(json['message']).to eq("Find me in app/views/top/index.html.erb")
    end
  end
end

このテストを実行すると、タイトル通りのエラーが発生し、以下のようなメッセージがコンソールに表示されます。

ActionView::MissingTemplate: Missing template top/index, application/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:

因みに4行目の「render_views」を書かないと、Controllerを呼び出した結果の「response.body」が空になります。

このテストを実行するためには、8行目を以下のように記述し、フォーマットとしてJSONを指定します。

get 'index', { :format => 'json' }

8行目を書き換えた状態でテストをすると、コンソールに以下のように表示され、テストが正常に実行できたことが分かります。

1 example, 0 failures, 1 passed

Finished in 0.110006 seconds

ブラウザ

上記にも書いたとおり、原因はControllerを呼び出す際にフォーマットとしてJSONを指定していなかったことです。

なのでブラウザにてURLを指定し、Controllerを呼び出す際にも同様のことが起きるかを試してみました。

先ずはURLにてフォーマットを指定しないと・・・

error

次にURLにてフォーマットを指定すると(.jsonをつける)・・・

ok

と、無事にJSONを表示できました。

まとめ

JbuilderにてJSONを返却する際には、リクエストを出す側(RSpecやブラウザ)もフォーマットを指定する必要があることが分かりました。
分かってしまえば単純なことですが、RSpecを先に書くテスト駆動的な開発を行っていたためか、以外に解決に時間が掛かりました。

P.S. 解決に協力して下さった先輩、ありがとうございました。

参考記事

Getting rspec to test controllers rendering JSON views
jayncoke / gist:4a7c1a67fdc464ad66e8