はじめに
消費税改正に備えるっていったって、お金のことなんだからテスト欲しいよねって思って、テストのほうも準備しました。
テストのソースは長くなりがちなので、こちらを見てみてください。
方針
テストはモジュールのほうでガッチリやって、モジュールをextendしたクラスのほうはメソッドが使えることくらいを確認するくらいにします。
モジュールのテスト
モジュールのテストで、ちょっとおもしろそうなところだけ、ピックアップします。
テストごとにちゃんと作り直されるクラス
クラス定義をlet
で変数のようにしてるところはちょっとおもしろいと思っています。RubyはクラスもClassクラスのオブジェクトなので、こんな書き方ができて、これだとテストごとに毎回定義されるので、いろいろとテストがやりやすくなったなあと思っています。
let(:klass) do
Class.new do
extend WithTax
attr_accessor :name, :price
attr_with_tax :price
def initialize(name, price)
@price = price
end
end
end
実行日の固定化
実行したときによって結果が変わるテストは、実行日を止めておいたほうがよいと思っています。
たまにmasterのCIが落ちてワタワタしますが、実行日を決めておかなかったことが原因だったことが一度や二度ではなかったです。
あと、戻し忘れてほかのテストが落ちたり落ちなかったりするようになることも多かったので、止めたらすぐに戻すコードを書く癖をつけています。
なお、ここではTimecopを使っていますが、railsを使っている場合であれば、travel_toなどがあるので、そちらがいいと思います。
before { Timecop.freeze(Date.parse(execution_date)) }
after { Timecop.return }
.attr_with_tax
がちゃんとメソッドを足せた?
respond_to
マッチャーで、attr_with_tax
を指定しているprice
にはprice_with_tax
が設定されていること、指定していないname
にはname_with_tax
が存在しないことを確認しています。
describe 'メソッドの追加' do
let(:sample_item) { klass.new('Sample Item', 123) }
it '#price_with_taxが存在すること' do
expect(sample_item).to respond_to(:price_with_tax).with(1).argument
end
it '#name_with_taxが存在しないこと' do
expect(sample_item).not_to respond_to(:name_with_tax)
end
end
追加したメソッドはちゃんと動いてる?
足されることは確認できたので、今度は足されているメソッドが期待通り動くのか確認します。
このあたりは、ごく普通のテストみたいに書けました。
小数点以下の処理
小数点以下を切り捨てる仕様にしたので、小数点以下が0.5未満の例と0.5以上の例を挙げて確認しました。
describe '小数点以下の処理' do
subject { sample_item.price_with_tax }
let(:sample_item) { klass.new('Sample Item', price) }
context 'price = 123のとき' do
let(:price) { 123 }
it '135.3->135(切り捨て)になること' do
is_expected.to eq 135
end
end
context 'price = 345のとき' do
let(:price) { 345 }
it '379.8->379(切り捨て)になること' do
is_expected.to eq 379
end
end
end
税率
税率は実行日や指定日によって変わります。
2019/10/01の改正で10%になったので、その前日は8%で計算されていることなど、改正日の前後をピックアップして値のテストを行いました。
中身は長いのでこちらでみてください。
モジュールを利用しているクラスのテスト
モジュールのほうでがっちりテストしているので、ここでのテストは#price_with_tax
が存在することを確認するのみとしました。
RSpec.describe SampleItem do
describe '#price_with_tax' do
subject { SampleItem.new('Sample Item', 123) }
it '#price_with_taxが利用できること' do
is_expected.to respond_to(:price_with_tax).with(1).argument
end
end
end
最後に
先にも書きましたが、「クラスはClassクラスのオブジェクト」が好きで、こういうのを書くとRubyっていいなって実感します。