shared_examples_forに引数を渡して処理をさらにまとめる

ここ最近はリファクタリングやより短い書き方を考えてます。

一行でもコードから文字が減るのは嬉しい。

そんなわけで、カラム名が違うだけで、内容同じじゃん・・・って場合のテストの書き方です。

前提条件

カラムの:login, :passwordは半角英数字のみで文字数制限を含んでいます。 ただし、文字数制限は違います。 けど、やってることは同じなので、処理をまとめます。

shared_examples_forの書き方

shared_examples_for 'alphanumeric' do |column_name, min_len, max_len|
  let(:factory_name) { described_class.name.underscore }
  let(:record) { build(factory_name) }

  context "#{column_name}" do
    context '登録できる' do
      context '半角英数字' do
        it '_を含むもの' do
          record[column_name] = 'foo_'
          expect(record).to be_valid
        end

      context '文字列の長さ' do
        it "#{min_len}文字" do
          record[column_name] = 'a' * min_len
          expect(record).to be_valid
        end

        it "#{max_len}文字" do
          record[column_name] = 'a' * max_len
          expect(record).to be_valid
        end
      end
    end
  end
end

こんな感じですかね。

ここでのポイントはrecord[column_name]で値を入れるようにすることです。

record.column_nameではno method errorになります。

あとはit_behaves_likeに引数を渡します。

it_behaves_like 'alphanumeric', :login, 4, 20
it_behaves_like 'alphanumeric', :password, 8, 20

これで同じ処理をまとめることができました。

ただ、passwordpassword_confirmationがいるよ!ってエラーになるので、そこはif文で分岐処理を書きました。

context '半角英数字' do
  it '_を含むもの' do
    value = 'foo_'
    if column_name == :password
      expect(record.update(password: value,
                           password_confirmation: value)).to be_truthy
    else
      record[column_name] = value
      expect(record).to be_valid
    end
  end
end

ここをうまく書ける方法があれば教えていただきたいですm( )m