読者です 読者をやめる 読者になる 読者になる

株式会社ネクスト エンジニアBlog

不動産・住宅情報サイト HOME'Sを運営する株式会社ネクストのエンジニアが提供する技術ブログです。エンジニアに役立つ情報の発信や、弊社エンジニアの活動を中心にお届けします。

rspec でファイル読み書きテスト

ネクストでレコメンドエンジン開発をしてる古川です。

rubyで、ファイルを読み込んで加工して別のファイルに出力というプログラムをよく書きます。 最近、rspec でテストを書くようになったのですが、beforeでテスト入力ファイルを作成し、 after で作成したテスト入力ファイル、テスト出力ファイルを削除、ということをしていました。

とりあえずは、これで問題なかったのですが、同時実行時や、実行時パス、パーミッションなど、 今後いろいろ問題になりそうで、いやだなと思っていたところ、Stack Overflowに、 まさに、それがやりたかったんだよ!という記事を見つけました。 StringIOを使えばよかったのですね。

記事は、読み込みテストだけでしたので、書き込みテストも追加してみました。

テスト対象クラス my_file_io.rb

class MyFileIo
  def run(path_src, path_dst)
    x = load(path_src)
    y = transform(x)
    save(y, path_dst)
  end
  def load(path)
    fp = ::File.open(path, 'r')
    data = []
    fp.each do |line|
      line.chop!
      data.push(line.split("\t"))
    end
    fp.close

    data
  end

  def transform(x)
    x.map {|t| t.map {|s| s.gsub(/a/, 'b')}}
  end

  def save(y, path)
    fp = ::File.open(path, 'w')
    y.each do |x|
      fp.write(x.join("\t") + "\n")
    end
    fp.close
  end
end

spec ファイル my_file_io_spec.rb

describe MyFileIo do
  let(:target) { MyFileIo.new }

  it '元ファイルのa が bに置き換わったファイルが作成されること' do
    path_src = 'src.tsv'
    path_dst = 'dst.tsv'
    data_src = [
                ['a11', 'a12'],
                ['a21', 'a22']
               ]
    data_dst = [
                ['b11', 'b12'],
                ['b21', 'b22']
               ]
    contents_src_file = (data_src.map {|x| x.join("\t")}).join("\n") + "\n"

    ::File.stub(:open)
      .with(path_src, 'r')
      .and_return(::StringIO.new((contents_src_file), 'r'))

    file_dst = ::StringIO.new('', 'w')
    ::File.stub(:open)
      .with(path_dst, 'w')
      .and_return(file_dst)

    ans = (data_dst.map {|x| x.join("\t")}).join("\n") + "\n"
    target.run(path_src, path_dst)
    file_dst.string.should eq(ans)
  end
end

Stack Overflow いつも、お世話になってます。