The $stdin is a global variable that holds a stream for the standard input. It can be used to read input from the console. reading.rb. #!/usr/bin/ruby inp = $stdin.read puts inp. In the above code, we use the read method to read input from the console.
Most methods can be tested by saying, “When I pass in argument X, I expect return value Y.” This one isn't so straightforward though. This is more like “When the user sees output X and then enters value V, expect subsequent output O.” Instead of accepting arguments, this method gets its value from user input.
I'm currently trying to test a basic method that receives some input from the user (gets) and outputs it (puts). After a bit of research I found a good way to test the standard output stream which is the below:
def capture_standard_output(&block)
original_stream = $stdout
$stdout = mock = StringIO.new
yield
mock.string.chomp
ensure
$stdout = original_stream
end
The method I'm testing is the one below, output and input refer to ivars which I initialize in the beginning and point to the equivalent $stdout & $stdin:
def ask_for_mark
ouput.puts 'What shall I call you today?'
answer = input.gets.chomp.capitalize
answer
end
Now I've seen some solutions for STDIN but haven't really understood any of them and I definitely don't want to copy & paste. The only one I got to "work" is the one below, but it's not really working since when I run rspec it pauses and waits for input and by simply pressing enter, it passes:
it "takes user's 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
What would be a good way to test STDIN? I've been staring at a screen for most of the day (not good, I know) so a fresh perspective and some help would be greatly appreciated :-)
For anyone facing similar issues, I followed different (simpler) approaches. First, I would separate the IO operations in their own methods.
In the case of input, in the tests it can be pre-populated with the desired data so when the gets
message is send, it will return that data which are separated by the newline character \n
like so:
input = StringIO.new("one\ntwo\n")
=> #<StringIO:0x007f88152f3510>
input.gets
=> "one\n"
input.gets
=> "two\n"
input.gets
=> nil
This helps with keep the internals of the method under test, private without coupling the tests to the implementation details.
Another approach is to simply use polymorphism and pass in a Fake or a Spy object that conforms to the same api but instead of making a call to stdin
or stdout
, it returns canned data instead or in the case of the Spy, it registers the call.
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
There's tradeoffs in each approach but I thought I'd put these here in case anyone was having similar issues or considering options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With