LoginSignup
7

More than 3 years have passed since last update.

rspec-parameterizedを使って、RSpecをリファクタリングした話

Last updated at Posted at 2018-12-11

この記事は、Business Bank Group Developers Advent Calendar 12日目の記事です。

前書き

ビジネスロジックのspecを書いている時に、specファイルが肥大化してしまい、メンテナンスが辛くなってきたので、rspec-parameterizedを使ってリファクタリングをしたよ。という話です。

元のspec

このようなspecを書いていました。(本当は、もう少し複雑です)

before
describe "#validate" do
  subject { importer.validate(column_type, condition) }

  let(:column_type) { ColumnType.find_by_code(code) }
  let(:condition) { setting: setting, amount: amount }

  %w(a b c e).each do |code|
    context "when column_type is '#{code}'" do
      context "when setting is true" do
        let(:setting) { true }

        context "when amount blank" do
          let(:amount) { nil }
          it { expect(subject).to eq nil }
        end

        context "when amount present" do
          let(:amount) { 2 }
          it { expect(subject).to eq nil }
        end

        context "when amount is invalid format" do
          let(:amount) { "ABC" }
          it { expect(subject).to eq "invalid_format" }
        end
      end

      context "when setting is false" do
        let(:setting) { false }

        context "when amount nil" do
          let(:amount) { nil }
          it { expect(subject).to eq nil }
        end

        context "when amount present" do
          let(:amount) { 2 }
          it { expect(subject).to eq "invalid_setting" }
        end
      end
    end
  end

  %w(d f).each do |code|
    # contexts(長くなるので割愛)
  end
end

...条件が増えるごとにcontextを増やさないといけないのでしょうか。書いてて嫌になりそうです(笑)
更に、MECEを考える時にパターンの漏れが発生しやすそうです...

rspec-parameterizedを使ってみた

rspec-parameterizedのNested Array Styleを用いて、このようにリファクタリングできました。

after
describe "#validate" do
  subject { importer.validate(column_type, condition) }

  let(:condition) { setting: setting, amount: amount }
  let(:column_type) { ColumnType.find_by_code(code) }

  %w(a b c e).each do |code|
    context "when column_type is '#{code}'" do
      where(:setting, :amount, :result) do
        [
          [true ,nil, nil],
          [true ,2, nil],
          [true ,"ABC", "invalid_format"],
          [false ,nil, nil],
          [false ,2, "invalid_setting"],
          [false ,"ABC", "invalid_setting"]
        ]
      end

      with_them { it { expect(subject).to eq result } }
    end
  end

  %w(d f).each do |code|
    # contexts(割愛)
  end
end

rspec-parameterizedの書き方については、下記記事でまとめています。
rspec-parameterized ことはじめ

どうなったか

  • コードの行数が約1/3に減った
  • 行数が減ったことによりコードが見やすくなった
  • コードが見やすくなったことにより、specで仕様を表せていることがより可視化できた

まとめ

FizzBuzzなど、簡単なテストであればまだ可愛いですが、実際のビジネスロジックでは、各ステータス(条件)毎の〇〇を求めるメソッドなど、複雑な条件を網羅するテストを書く必要があり、rspec-parameterizedは、それを書きやすく、より分かりやすくさせるものでした!

明日は @Kirika さんが担当です。よろしくお願いします!

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
What you can do with signing up
7