使用 rspec 和普通 ruby 测试 input/output

Testing input/output with rspec and plain ruby

我正在尝试为从文本文件读取的 FileProcessor 创建一个测试,将其传递给另一个 class,然后写入输出。我制作了一个测试文件并且可以访问,但感觉很笨重。我还需要测试它是否将输出写入新文件,但我不确定如何设置它。我看过很多教程,但它们都是以 rails 为中心的。我的目标是摆脱在测试中编写路径并在每次测试后清理生成的输出文件。

describe FileProcessor do 

  test_file = File.dirname(__FILE__) + '/fixtures/test_input.txt'
  output_file = File.dirname(__FILE__) + '/fixtures/test_output.txt'

  subject {FileProcessor.new(test_file, output_file)}

  describe '#read_file' do
    it 'reads a file' do
      expect(subject.read_file).to eq('This is a test.')
    end
  end

  def write_file(str)
   File.open("#{output_file}", "w+") { |file| file.write(str) }
  end


end

没有什么好方法可以避免写入输入文件的路径。您可以将其移至辅助方法中,但另一方面,在测试中使用路径的好处是其他人(或六个月后的您)查看代码将立即知道测试数据的来源。

至于输出文件,最简单的解决方案是使用Ruby的内置Tempfile class。 Tempfile.new 类似于 File.new,除了它会自动将文件放在 /tmp 中(或 OS 的临时文件目录所在的任何位置)并为其指定一个唯一的名称。这样你就不必担心清理它,因为下次你 运行 测试时它会使用一个不同名称的文件(你的 OS 会自动删除该文件) .例如:

require 'tempfile'

describe FileProcessor do
  let(:test_file_path) { File.dirname(__FILE__) + '/fixtures/test_input.txt' }
  let(:output_file) { Tempfile.new('test_output.txt').path }

  subject { FileProcessor.new(test_file_path, output_file.path) }

  describe '#read_file' do
    it 'reads a file' do
      expect(subject.read_file).to eq('This is a test.')
    end
  end
end

使用let(而不是仅仅分配一个局部变量)确保每个示例都将使用其自己唯一的输出文件。在 RSpec 中,你应该几乎总是喜欢 let

如果你想认真一点,你可以改用 FakeFS gem, which mocks all of Ruby's built-in file-related classes (File, Pathname, etc.) so you're never writing to your actual filesystem. Here's a quick tutorial on using FakeFS: http://www.bignerdranch.com/blog/fake-it/

使用StringIO怎么样:

require 'stringio'

class FileProcessor

  def initialize(infile, outfile)
    @infile = infile
    @outfile = outfile
    @content = nil
  end

  def read_file
    @content ||= @infile.read
  end

  def write_file(text)
    @outfile.write(text)
  end
end

describe FileProcessor do 
    let(:outfile) { StringIO.new }

    subject(:file_processor) do
      infile = StringIO.new('This is a test')
      FileProcessor.new(infile, outfile)
    end

    describe '#read_file' do
      it "returns correct text" do
        expect(file_processor.read_file).to eq("This is a test")
      end
    end

    describe '#write_file' do
      it "writes correct text" do
        file_processor.write_file("Hello world")

        outfile.rewind
        expect(outfile.read).to eq("Hello world")
      end
    end
end