5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.


この記事はAteam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020 18日目の記事です。


はじめに

「テストを書きやすいコードは、メンテナンス性に優れる」ということがよく言われます。
日常的にテストを書いておられる方々はその意味をご認識されていると思いますが、テスト初心者の方、テストを書いていこうと思っている方々に向けて、その具体例をご紹介したいと思います。

テストしにくいコードとメンテしにくいコード

sample.rb
class Hoge
  # 略
  def puts_by_weekday
    business_day = BusinessDay.new
    if business_day.holiday?
      p "今日は休日!!!!"
    else
      p "今日は仕事"
    end
  end
end

上記の例では、曜日によって処理を変えています。
一つのメソッドの中でTimeクラスから曜日を呼び出して条件分岐に使っています。
このような場合、おそらく下記のようなテストコードになるでしょうか

sample_spec.rb
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言語などではこの場合テストを書くのが非常に困難になるでしょう。

ではメンテナビリティの観点ではどうでしょうか?

例えば、下記のように条件分岐のパターンが増える場合を想定してみましょう。

sample.rb
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の原則を意識した実装をすることで、よりテストしやすく、より柔軟なコードを書くことができます。

sample.rb
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は以下のように書けます。

holiday_informer_spec.rb
describe "#inform" do
  subject { HolidayInformer.new.inform }
  it { is_expected.to eq "今日は休日!!!!" }
end
workday_informer_spec.rb
describe "#inform" do
  subject { WorkdayInformer.new.inform }
  it { is_expected.to eq "今日は仕事" }
end

このように各クラスごとにメソッドを実装しているので、Rspecもそれぞれでテストを書いていくことができます。
スタブなどが使う必要がなく、そのような機能をサポートしていない言語でもテストを書くことが容易になります。

また、メンテナンスの観点からもSOLID原則にのっとることで恩恵があります。
条件分岐が増えても、新たにクラスを追加するだけでHogeクラスをいじる必要がなくなり、システムの安定性に寄与します。

最後に

いかがでしたでしょうか。
テストの書きやすいコードとメンテナンスのしやすいコードというのは共通点があると思います。
ぜひこういったことを取り入れてハッピーな開発できるようにしていきましょう!

次回

明日は @m-otsukaさんの記事です!お楽しみに!

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?