Edited at

Rails4のconcernsなmoduleのテストをrspecで書く方法

More than 3 years have passed since last update.

Everyday Rails - RSpecによるRailsテスト入門」を読んでRSpecが楽しくなってきたところで、この本に書かれていないけど「こんなのもテストしたい!」という場合にどうすればいいか調べてみた。

Rails4では、共通機能を定義したモジュールをconcernsに置くことができる。目的はDRYであったり、Fat Controller, Fat Modelを避けるため(だと思う)。Rails3以前はバラバラの方法で行われていたけど、オフィシャルの規約ができた(のだと思う)。

そこで、concerns下に定義したモジュールのテストをRSpecで書く方法を検討してみた。

いくつか方法があり、例えば、リア充爆発日記で紹介されていたりする。個人的にはStack Overflowで紹介されている方法が好みだったので、最小限のコードでまとめてみた。


テスト対象のコード


app/controllers/hoge.rb

module Hoge

def add(a, b)
a + b
end
end


テスト


spec/concerns/hoge_spec.rb

require 'spec_helper'

describe Hoge do

let(:test_class) { Struct.new(:hoge) { include Hoge } }
let(:hoge) { test_class.new }

describe ".add" do
it "1, 2を渡すと3を返す" do
expect(hoge.add(1, 2)).to eql 3
end
end
end


ポイントはRubyのStructを使っているところ。肝心のテスト部分の見通しが良いし、セットアップ(let)も2行で収まっておりシンプル。ケースバイケースなところはあるけど、テスト対象が簡単なユーティリティメソッドだったらこの方法が簡単な気がした。

spec/concernsの下にテストを作っておくことで、 bundle exec rspec で走らせた時にテストしてくれる。なお、この構成だと、app/controllers/concernsのテストとapp/models/concernsのテストが共存することになる。ただ、そもそもconcerns下のmoduleを多用していないので、今のところ問題にはなっていない。

また、Struct.new してる時に :foo を渡しているのは、引数なしだと下のようなエラーになったため。

     Failure/Error: let(:test_class) { Struct.new() { include Hoge } }

ArgumentError:
wrong number of arguments (0 for 1+)
# ./spec/concerns/hoge_spec.rb:5:in `new'
# ./spec/concerns/hoge_spec.rb:5:in `block (2 levels) in <top (required)>'
# ./spec/concerns/hoge_spec.rb:6:in `block (2 levels) in <top (required)>'
# ./spec/concerns/hoge_spec.rb:10:in `block (3 levels) in <top (required)>'