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

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

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

前提条件

カラムの: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