有没有打开一个 StringIO 来写这样的东西?

Is there such a thing as opening a StringIO for writing?

我正在尝试使用 RSpec 来测试写入文件的 class。但我希望测试速度更快,所以我不想使用真实文件并写入磁盘,而是想在测试中使用 StringIO 并写入内存。

以一种非常简单的方式,假设我有这个测试:

RSpec.describe Writer do
  it 'replaces the contents of the file' do
    file = StringIO.new('foo')
    writer = described_class.new(file)
    one_contact = [{ 'name' => 'name', 'address' => 'address' }]

    writer.write(one_contact)

    expect(file.string).to eq('[{"name":"name1","address":"address1"}]')
  end
end

假设作者 class 是这样的:

require 'json'

class Writer
  def initialize(file)
    @file = file
  end

  def write(contacts)
    file.truncate(0)
    file.write(contacts.to_json)
    file.flush
  end

  private

  attr_reader :file
end

当我 运行 通过 Rspec 测试时,我得到错误:

 Failure/Error: file.truncate(0)

 IOError:
   not opened for writing

如果我将 truncate 更改为其他内容,那么我会在 file 上调用 write 的行中收到此错误。

但是,如果我撬开它,它会起作用:

$ pry
[1] pry(main)> require_relative 'lib/db/writer'
=> true
[2] pry(main)> file = StringIO.new('foo')
=> #<StringIO:0x0000563c99220f70>
[3] pry(main)> writer = Writer.new(file)
=> #<Writer:0x0000563c98ff8950 @file=#<StringIO:0x0000563c99220f70>>
[4] pry(main)> one_contact = [{ 'name' => 'name', 'address' => 'address' }]
=> [{"name"=>"name", "address"=>"address"}]
[5] pry(main)> writer.write(one_contact)
=> #<StringIO:0x0000563c99220f70>
[6] pry(main)> file.string
=> "[{\"name\":\"name\",\"address\":\"address\"}]"

如果我在普通 ruby 文件中执行此操作,并且 运行 使用 ruby,例如:

require_relative 'lib/db/writer'

file = StringIO.new('foo')
writer = Writer.new(file)

one_contact = [{ 'name' => 'name1', 'address' => 'address1' }]
writer.write(one_contact)
puts file.string

同样有效。

这是一个超级简单的 Ruby 应用程序,没有依赖项或框架,这就是 Gemfile 的样子:

source 'https://rubygems.org'
ruby '2.5.1'

gem 'sqlite3'

group :test do
  gem 'coveralls', require: false
  gem 'pry'
  gem 'rake'
  gem 'rspec'
  gem 'rubocop', require: false
end

实际 gem 版本:

如何打开一个StringIO进行写入?我想在测试时继续写入内存而不是磁盘。

这是你的问题:

# frozen_string_literal: true

您的字符串 contents 已冻结,无法修改。 StringIO用上面的IOError.

表示