Ошибка rspec 3, когда пользователю предлагается ввести данные

У меня есть код, который запрашивает пользователя для ввода, например:

class Foo
  def prompt_for_foobar
    puts "where is the foobar?"
    gets.chomp
  end
end

Я хотел бы проверить, что мое приложение спрашивает "где находится foobar?". Мой тест пройдет после комментирования 'gets.chomp'. но это необходимо, и все, что я пробовал, дало мне ошибку Errno::ENOENT:.

it "should prompt user" do
  console = Foo.new
  request = "where is the foobar?"
  expect { console.prompt_for_foobar }.to output(request).to_stdout
end

Каков наилучший способ проверить этот метод?

2 ответа

Проблема в том, что gets это метод, который вызывает взаимодействие с человеком (по крайней мере, в контексте RSpec, где stdin не подключен к каналу из другого процесса), но весь смысл инструментов автоматизированного тестирования, таких как RSpec, должен быть в состоянии запускать тесты без участия человека взаимодействие.

Таким образом, вместо того, чтобы полагаться непосредственно на gets в вашем методе я рекомендую вам полагаться на объект соавтора, который реализует определенный интерфейс - таким образом, в тестовой среде вы можете обеспечить реализацию этого интерфейса, который обеспечивает ответ без взаимодействия с человеком, а в других средах, которые он может использовать gets предоставить ответ. Простейшим интерфейсом для коллабораторов здесь, вероятно, является proc (они идеально подходят для такого рода вещей!), Поэтому вы можете сделать следующее:

class Foo
  def prompt_for_foobar(&responder)
    responder ||= lambda { gets }
    puts "where is the foobar?"
    responder.call.chomp
  end
end

RSpec.describe Foo do
  it 'prompts the user to respond' do
    expect { Foo.new.prompt_for_foobar { "" } }.to output(/where is the foobar/).to_stdout
  end

  it "returns the responder's response" do
    expect(Foo.new.prompt_for_foobar { "response" }).to eq("response")
  end
end

Заметить, что prompt_for_foobar больше не звонит gets непосредственно; вместо этого он делегирует ответственность за получение ответа на responder Соавтор. По умолчанию, если ответчик не предоставлен, он использует gets по умолчанию реализация респондента. В своем тесте вы можете легко предоставить респондент, который не требует вмешательства человека, просто передав блок, который возвращает строку.

Не уверен, что это лучший способ справиться с этим, но вы можете отправить puts а также gets в STDOUT а также STDIN,

class Foo
  def prompt_for_foobar
    STDOUT.puts "where is the foobar?"
    STDIN.gets.chomp
  end
end

Затем проверьте это STDIN получает это puts сообщение с нужным вам объектом.

describe Foo do
  let(:foo) { Foo.new }

  before(:each) do
    allow(STDIN).to receive(:gets) { "user input" }
  end

  describe "#prompt_for_foobar" do
    it "prompts the user" do
      expect(STDOUT).to receive(:puts).with("where is the foobar?")

      foo.prompt_for_foobar
    end

    it "returns input from the user" do
      allow(STDOUT).to receive(:puts)

      expect(foo.prompt_for_foobar).to eq "user input"
    end
  end
end
Другие вопросы по тегам