By

rablのテスト方法

ビジネスバンクグループエンジニアの 栗山 宗久 です。

RailsでJson APIを定義する時にto_jsonやas_jsonで:onlyや:includeオプションを使ってフィールドを指定できます。
ただ、これだとControllerやAPIを定義しているクラスに依存してしまい、また可読性もあまり良くありません。
そこで、簡潔に構造を定義するためのrablを使用します。

今回は、そのrablのテスト方法についてまとめます。
 

手順

  1. テストデータのオブジェクトを生成する
  2. Rabl::Renderer#renderを使用し、テスト対象のrabl、生成したオブジェクトを指定し、jsonを取得する
  3. 取得したjsonをJson#parseでHashにする
  4. 想定するテストデータのハッシュ値とJson#parseで生成されたデータの整合性を確認する

 

RABLの定義

例として次のようなRABLを作成するとします。

#app/views/quizzes/show.rabl
object @quiz
attribute :title
attribute :content
node(:point) { |o| @level * o.base_point }

このrablを使用して生成されるjsonは次のようになります。

{
  "title" : "Mame chishiki",
  "content": "Shigoto no gogen ha?",
  "point": 80
}

記述したrablのテストを行います。 json形式の場合、エラーの内容が判別しにくいので、jsonで生成されたデータをHash化して確認します。
 

Rabl::Renderer#render

Rabl::Rendererクラスを使用することで、テストを行うrablのjsonを生成することができます。

  # https://github.com/nesquena/rabl/lib/rabl/renderer.rb
  # Public: Instantiate a new renderer
  # This is a standalone class used for rendering rabl templates
  # outside of a framework like Rails. You may want to use
  # this when using Rabl to render the request objects passed to
  # message queues.
  #
  # Example:
  #   renderer = Rabl::Renderer.new('template_name', user, { :format => 'json', :view_path => 'app/views' })
  #   renderer.render # => '{"user":{"name": "ivan" }}'
  #
  def initialize(source, object = nil, options = {})
    options = {
      :format     => :json,
      :scope      => self,
      :view_path  => [],
      :template   => source
    }.merge(options)

    @options  = options
    @object   = object

    engine.source = process_source(source)
  end

  # Public: Actually render the template to the requested output format.
  #
  # - context_scope:
  #     Override the render context_scope to the 'context_scope' object. Defaults to self.
  #
  # Returns: And object representing the tranformed object in the requested format.
  #   e.g. json, xml, bson, plist
  def render(context_scope = nil)
    context_scope ||= options[:scope] || self

    set_object_instance_variable if context_scope == self

    locals = { :object => object }.merge(options.fetch(:locals, {}))

    engine.apply(context_scope, locals).render
  end

initializeのparameterについては、下記のような内容を設定できます。

  • source ・・・ rablファイル名
  • object ・・・ rablでパースするオブジェクト
  • options ・・・ オプションを指定可能
    • format ・・・ フォーマットを指定
    • scope ・・・ object以外のインスタンス変数を使用している際に利用
    • view_path ・・・ rablファイルまでのパス
    • template ・・・ テンプレート

 

テスト

例に上げたapp/views/quizzes/show.rablのテストを書くと次のようになります。

# rablで生成したJsonをHash化するためのモジュール
module RablSpecHelper
  extend ActiveSupport::Concern

  def render_as_hash(object, source, options = {})
    renderer = Rabl::Renderer.new(source, object, options)
    JSON.parse(renderer.render, symbolize_names: true)
  end
end
require "rails_helper"

describe "show.rabl" do
  include RablSpecHelper

  subject { render_as_hash(object, source, options) }

  # インスタンス変数@levelに値を渡すためにscopeを使用
  let(:scope) do
    scope = Object.new
    scope.instance_variable_set :@level, 2
    scope
  end
  let(:source) { "show" }
  let(:object) do
    OpenStruct.new(title: "Mame chishiki", content: "Shigoto no gogen ha?", base_point: 40)
  end
  let(:options) do
    {
      format: "json",
      scope: scope,
      view_path: "app/views/quizzes"
    }
  end
  let(:expected) do
    {
      title: "Mame chishiki",
      content: "Shigoto no gogen ha?",
      point: 80
    }
  end

  it { is_expected.to eq expected }
end

 

rablのテスト方法を調べてみて

あまり日本語の記事を見かけなかったので、最初インスタンス変数が入っている場合の綺麗なテスト方法が分かりませんでした。
なので、本元のライブラリのテストを覗き、libを調べて作成しました。gemの場合は、そのプロジェクトのspecを見るのが一番理解が早いですね!

nesquena/rabl

 

エンジニア募集中!

ビジネスバンクグループではエンジニアを募集中しています。

弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!