Есть ли такая вещь, как открытие StringIO для записи?

Я пытаюсь использовать RSpec для тестирования класса, который записывает в файл. Но я хочу, чтобы тесты были быстрыми, поэтому вместо использования реального файла и записи на диск я хочу использовать 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

и скажем, класс писателя выглядит так:

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 на что-то другое, я получу эту ошибку в строке, которая вызывает write на file.

Однако, если я сделаю это в pry, это сработает:

$ 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

Актуальные версии драгоценных камней:

  • sqlite3 (1.3.13)
  • комбинезон (0.8.22)
  • подглядывать (0.11.3)
  • грабли (12.3.1)
  • рспек (3.7.0)
  • рубокоп (0.58.1)

Как я могу открыть StringIO для записи? Я хочу продолжать писать в память, а не на диск при тестировании.


person Xirux Nefer    schedule 24.07.2018    source источник
comment
Не могу воспроизвести ваш, если я сделаю это в обычном рубиновом сценарии. Работает как надо. Возможно ли, что вы каким-либо образом переопределяете StringIO?   -  person Sergio Tulentsev    schedule 24.07.2018
comment
Удивительно, но нормальный рубин теперь работает. Но тест RSpec по-прежнему не работает. Я обновил свой вопрос.   -  person Xirux Nefer    schedule 24.07.2018
comment
Перезапустите spring, если вы его используете. Это было источником многих загадочных проблем для меня.   -  person Sergio Tulentsev    schedule 24.07.2018
comment
@SergioTulentsev спасибо, я не использую Spring. Это очень простое рубиновое приложение. Я обновил свой вопрос.   -  person Xirux Nefer    schedule 24.07.2018
comment
если вы опубликуете реальный работоспособный и воспроизводимый пример на github, я посмотрю. Однако не публикуйте все свое приложение. Сократите до абсолютного минимума то, что a) по-прежнему воспроизводит проблему, а b) является завершенным и работоспособным. Я (или кто-то другой) должен иметь возможность просто оформить заказ, связать и запустить rspec.   -  person Sergio Tulentsev    schedule 24.07.2018
comment
^ это называется минимально воспроизводимым примером, кстати.   -  person Sergio Tulentsev    schedule 24.07.2018
comment
@SergioTulentsev спасибо, я перешел на github.com/crocodileDundee/foo, он содержит только писатель и автор тестирует, просто клонируйте его и запустите bundle и rspec   -  person Xirux Nefer    schedule 24.07.2018


Ответы (1)


Вот ваша проблема:

# frozen_string_literal: true

Ваша строка contents заморожена и не может быть изменена. StringIO выражает это с помощью вышеупомянутого IOError.

person Sergio Tulentsev    schedule 24.07.2018