LoginSignup
7
7

More than 5 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 さんが担当です。よろしくお願いします!

7
7
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
7
7