LIFULL Creators Blog

LIFULL Creators Blogとは、株式会社LIFULLの社員が記事を共有するブログです。自分の役立つ経験や知識を広めることで世界をもっとFULLにしていきます。

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 いつも、お世話になってます。