rablのテスト方法
ビジネスバンクグループエンジニアの 栗山 宗久 です。
RailsでJson APIを定義する時にto_jsonやas_jsonで:onlyや:includeオプションを使ってフィールドを指定できます。
ただ、これだとControllerやAPIを定義しているクラスに依存してしまい、また可読性もあまり良くありません。
そこで、簡潔に構造を定義するためのrablを使用します。
今回は、そのrablのテスト方法についてまとめます。
手順
- テストデータのオブジェクトを生成する
- Rabl::Renderer#renderを使用し、テスト対象のrabl、生成したオブジェクトを指定し、jsonを取得する
- 取得したjsonをJson#parseでHashにする
- 想定するテストデータのハッシュ値と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を見るのが一番理解が早いですね!
エンジニア募集中!
ビジネスバンクグループではエンジニアを募集中しています。
弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!