この記事はAteam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020 18日目の記事です。
はじめに
「テストを書きやすいコードは、メンテナンス性に優れる」ということがよく言われます。
日常的にテストを書いておられる方々はその意味をご認識されていると思いますが、テスト初心者の方、テストを書いていこうと思っている方々に向けて、その具体例をご紹介したいと思います。
テストしにくいコードとメンテしにくいコード
class Hoge
# 略
def puts_by_weekday
business_day = BusinessDay.new
if business_day.holiday?
p "今日は休日!!!!"
else
p "今日は仕事"
end
end
end
上記の例では、曜日によって処理を変えています。
一つのメソッドの中でTimeクラスから曜日を呼び出して条件分岐に使っています。
このような場合、おそらく下記のようなテストコードになるでしょうか
describe "#puts_by_weekday" do
subject { Hoge.new.puts_by_weekday }
context "平日の時" do
before do
allow_any_instance_of(BusinessDay).to receive(:holiday?).and_return(false)
end
it { is_expected.to eq "今日は仕事" }
end
context "土日の時" do
before do
allow_any_instance_of(BusinessDay).to receive(:holiday?).and_return(true)
end
it { is_expected.to eq "今日は休日!!!!" }
end
end
御覧のように、条件分岐をテストしようとすると、Timeインスタンスのwdayメソッドをスタブしてそれぞれの条件でテストしなければなりません。
今回は二通りですが、パターンが増えるとしんどそうです。
また、言語によってはスタブやモックができないこともあります。
例えばGo言語などではこの場合テストを書くのが非常に困難になるでしょう。
ではメンテナビリティの観点ではどうでしょうか?
例えば、下記のように条件分岐のパターンが増える場合を想定してみましょう。
class Hoge
# 略
def puts_by_weekday
business_day = BusinessDay.new
if business_day.holiday?
p "今日は休日!!!!"
elsif business_day.public_holiday?
p "今日は祝日!!!!"
else
p "今日は仕事"
end
end
end
各条件分岐の中において、簡単のために文字列を出力しているだけですが、さらに条件分岐を重ねる必要のあるような要件だった場合、肥大化していくことは避けられないでしょう。また1つのメソッドに
このように、テストが書きにくいだけなく柔軟性に欠いた実装になってしまうことになりかねません。
SOLIDの原則を意識した実装
このような場合、SOLIDの原則を意識した実装をすることで、よりテストしやすく、より柔軟なコードを書くことができます。
class HolidayInformer
def initialize
end
def inform
p "今日は休日!!!!"
end
end
class WorkdayInformer
def initialize
end
def inform
p "今日は仕事"
end
end
class Hoge
def initialize(business_day_informer)
@business_day_informer = business_day_informer
end
def puts_by_weekday
@business_day_informer.inform
end
end
business_day = BusinessDay.new
if business_day.holiday?
business_day_informer = HolidayInformer.new
else
business_day_informer = WorkdayInformer.new
end
Hoge.new(business_day_informer).puts_by_weekday
休日と平日の処理をそれぞれクラスを用意してそちらで実装するようにし、Hogeクラスからはそのメソッドを呼ぶだけにしました。
この場合のRspecは以下のように書けます。
describe "#inform" do
subject { HolidayInformer.new.inform }
it { is_expected.to eq "今日は休日!!!!" }
end
describe "#inform" do
subject { WorkdayInformer.new.inform }
it { is_expected.to eq "今日は仕事" }
end
このように各クラスごとにメソッドを実装しているので、Rspecもそれぞれでテストを書いていくことができます。
スタブなどが使う必要がなく、そのような機能をサポートしていない言語でもテストを書くことが容易になります。
また、メンテナンスの観点からもSOLID原則にのっとることで恩恵があります。
条件分岐が増えても、新たにクラスを追加するだけでHogeクラスをいじる必要がなくなり、システムの安定性に寄与します。
最後に
いかがでしたでしょうか。
テストの書きやすいコードとメンテナンスのしやすいコードというのは共通点があると思います。
ぜひこういったことを取り入れてハッピーな開発できるようにしていきましょう!
次回
明日は @m-otsukaさんの記事です!お楽しみに!