tkymtk's blog

Ruby on Rails及びその周辺について調べたこと。Do whatever you want to do at your own pace.

RSpecで試行錯誤

RSpec--format documentation を付けて実行したときに、結果がいい感じになるように工夫した過程。(だいたい)

最初

describe MatchAddress do
   describe '#call' do
     describe '000-0000' do
       address = described_class.new('000-0000').call
       it { expect(address).to eq 'True' }
     end
  end
end

output

MatchAddress
  #call
    000-0000
      should eq "True"

outputは悪くないけど、他の数字のケースでspecを書くのがめんどくさそうだ。また、重複する処理もある。

次: shared_examples使ってみよう

shared_examples_for 'valid address' do
  let(:address) { described_class.new(input).call }
  it { expect(address).to eq 'True' }
end

describe MatchAddress do
  describe '#call' do
    describe '000-0000' do
      it_behaves_like 'valid address' do
        let(:input) { '000-0000' }
      end
    end
  end
end

output

MatchIPv4Address
  #call
    000-0000
      behaves like valid address
        should eq "True"

複数ケースを少しでもDRYに書けるようになったのはよくなったかもしれないが、outputがよくない。

次: カスタムマッチャ作ってみる

describe MatchAddress do
  describe '#call' do
     describe '000-0000' do
        it { is_expected.to be_a_valid_address }
     end
  end
end

RSpec::Matchers.define :be_a_valid_address do
  match do |actual|
    MatchAddress.new(actual).call == 'True'
  end
end

output 怒られる

MatchIPv4Address
  #call
    "000-0000"
      example at ./match_address_spec.rb:18 (FAILED - 1)

 1) MatchAddress#call 000-0000
     Failure/Error: it { is_expected.to be_a_valid_address }
     ArgumentError:
       wrong number of arguments (0 for 1)

ほんとはこうなって欲しかった

MatchAddress
  #call
    "000-0000"
    should be a valid address

原因は、describeは引数が文字列だとsubjectを置き換えないから。 is_expectedexpected(subject)が呼ばれているだけなので、カスタムマッチャのmatchのブロック引数には000-0000はこない。

describe MatchAddress do
  describe '#call' do
     describe :'000-0000' do
        it { is_expected.to be_a_valid_address }
     end
  end
end

'000-0000':'000-0000'とシンボルにしてみる。 output

MatchAddress
  #call
    "000-0000"
    should be a valid address

できた。でも:'000-0000'とか微妙だし、ケースごとにdescribe書くのもなんだかな。

describeを削ってみる

describe MatchAddress do
  describe '#call' do
    specify('000-0000') { expect('000-0000').to be_a_valid_address }
  end
end

すっきりした。でも output

MatchAddress
  #call
    "000-0000"

わからん。it(specify)は、引数があるとoutputにその文字列が、ないと中身(ここではshould be a valid address)が表示される。 でも書き方は気に入った。

次: 000-00002回書くのが嫌だな

 it { expect(address).to be_a_valid_IPv4_address }

にしてみる output

MatchAddress
  #call
    should be a valid address

なるほど、わからん。

引数があるとoutputにその文字列が、ないと中身(ここではshould be a valid address)が表示される。

だからだ。そして、暗黙的なsubjectは表示されない。

結論

describe MatchAddress do
  describe '#call' do
    describe 'valid addresses' do
      %w(000-0000 2551024).each do |address|
        specify(address) { expect(address).to be_a_valid_address }
      end
    end
end

RSpec::Matchers.define :be_a_valid_address do
  match do |actual|
    MatchAddress.new(actual).call == 'True'
  end
end

output

MatchAddress
  #call
    valid addresses
      000-0000
      2551024

まぁ、少しoutputがイレギュラーな感じもするけど、分かるからいいんじゃないだろうか。 最終的にこんな感じで表示されると思う。

MatchAddress
  #call
    valid addresses
      000-0000
      2551024
    invalid addresses
      000ー0000
      000-000
      000-00000

その他

MatchAddress
  #call
    000-0000
      should be a valid address
    255-1024
      should be a valid address

みたいな結果がいい人は

describe MatchAddress do
  describe '#call' do
    %w(000-0000 255-1024).each do |address|
      describe address do
        # 明示的に`subject`を指定しないと`MatchAddress`が呼びだされる。
        subject { address }
        it { is_expected.to be_a_valid_address }
      end
    end
  end
end

もありかもしれない。でも、テストでeach使わない方が良いと思うなどの場合は、少し長い書き方だ。

感想

カスタムマッチャ、あまりつかってなかったけど、--format documentationで実行したら必要性と便利さがわかった。 今度から使おう。

間違いがあれば、ご指摘下さると幸いです。