В настоящее время я пытаюсь протестировать базовый метод, который получает некоторый ввод от пользователя (получает) и выводит его (puts). После небольшого исследования я нашел хороший способ протестировать стандартный выходной поток, который ниже:
def capture_standard_output(&block)
original_stream = $stdout
$stdout = mock = StringIO.new
yield
mock.string.chomp
ensure
$stdout = original_stream
end
Метод, который я тестирую, приведен ниже, вывод и ввод относятся к ivars, которые я инициализирую в начале и указываю на эквивалентные $stdout и $stdin:
def ask_for_mark
ouput.puts 'What shall I call you today?'
answer = input.gets.chomp.capitalize
answer
end
Теперь я видел некоторые решения для STDIN, но на самом деле не понял ни одного из них, и я определенно не хочу копировать и вставлять. Единственное, что я получил, чтобы "работать", - это тот, что приведен ниже, но он не работает с тех пор, как я запускаю rspec, он останавливается и ждет ввода и просто нажав enter, он передает:
it "takes user name and returns it" do
output = capture_standard_output { game.ask_for_name }
expect(output).to eq "What shall I call you today?"
game.input.stub(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
Что было бы хорошим способом тестирования STDIN? Я смотрел на экран большую часть дня (не очень хорошо, я знаю), поэтому свежий взгляд и некоторая помощь были бы весьма полезны: -)
Update
Для тех, кто сталкивается с подобными проблемами, я придерживался других (более простых) подходов. Во-первых, я бы отделил операции ввода-вывода в своих собственных методах.
В случае ввода в тестах он может быть предварительно заполнен нужными данными, поэтому при отправке сообщения gets
он вернет те данные, которые разделены символом новой строки \n
следующим образом:
input = StringIO.new("one\ntwo\n")
=> #<StringIO:0x007f88152f3510>
input.gets
=> "one\n"
input.gets
=> "two\n"
input.gets
=> nil
Это помогает сохранить внутренности тестируемого метода, приватно, не связывая тесты с деталями реализации.
Другой подход - просто использовать полиморфизм и передать объект Fake или Spy, который соответствует одному и тому же api, но вместо того, чтобы делать вызов stdin
или stdout
, он возвращает законченные данные вместо или в случае Spy, он регистрирует вызов.
class SpyIO
def initialize
was_called? = false
end
...
def ask_for_name
// call to stdout would normally take place
was_called? = true
end
...
end
class FakeIO
def initialize(data = [some, data])
@data = data
end
def get_user_input
// call to stdin would normally happen
@data.shift
end
...
end
Там есть компромиссы в каждом подходе, но я думал, что поставил бы их здесь, если бы у кого-то были подобные проблемы или варианты.