Testing STDIN in Ruby
You can simply stub STDIN:
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?"
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
Actually, you can do the same with STDOUT
, without needing to change $stdout
:
it "takes user's name and returns it" do
expect(STDOUT).to receive(:puts).with("What shall I call you today?")
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
how to test STDIN with RSpec
Because move_computer
returns the input, I think you meant to say:
player.move_computer("O").should == "\n"
I would write the full spec like this:
describe Player do
describe "#move_computer" do
it "returns a line from stdin" do
subject.stub!(:gets) {"penguin banana limousine"}
STDOUT.should_receive(:puts).with("computer move")
subject.move_computer("O").should == "penguin banana limousine"
end
end
end
How to test stdin for a CLI using rspec
I ended up finding a solution that I think fairly closely mirrors the code for executing instructions from a file. I overcame the main hurdle by finally realizing that I could write cli.stub(:gets).and_return
and pass it in the array of commands I wanted to execute (as parameters thanks to the splat *
operator), and then pass it the "EXIT"
command to finish. So simple, yet so elusive. Many thanks go to this StackOverflow question and answer for pushing me over the line.
Here is the code:
describe "executing instructions from the command line" do
let(:output) { capture(:stdout) { cli.execute } }
context "with valid commands" do
valid_test_data.each do |data|
let(:expected_output) { data[:output] }
let(:commands) { StringIO.new(data[:input]).map { |a| a.strip } }
it "should process the commands and output the results" do
cli.stub(:gets).and_return(*commands, "EXIT")
output.should include(expected_output)
end
end
end
# ...
end
Can I use RSpec to mock stdin/stdout to test console reads & writes?
You can use mocks and have the method called more than once by listing multiple values in the and_return()
method. These will be returned, one on each call, in the order given.
STDIN.should_receive(:read).and_return("Your string")
STDIN.should_receive(:read).and_return("value1", "value2", "value3")
You can do similar things with STDOUT:
STDOUT.should_receive(:puts).with("string")
See the RSpec mocking documentation for more information.
Testing ruby with rspec for file name and STDIN with while gets
How about this idea?
[EDITED]
For the main script…
require 'processor'
sources = ARGV.map{ |fname| File.open(fname) }
sources = [$stdin] if sources.empty?
Processor.new(sources).process
…and for processor.rb…
Class Processor
attr_reader :sources
attr_accessor :card_hash
def initialize(sources)
@sources = sources
@card_hash = Hash.new
end
def process
sources.each do |source|
source.each_line.do |line|
...
Then, for testing purposes, you can pass an array of one or more stub objects for the sources. The stub objects could be something like RSpec double
s or an instances of StringIO
. See http://apidock.com/ruby/StringIO .
Testing gets in rspec (user input)
You can avoid this as such:
def run
puts "Enter 'class' to create a new class."
input = gets.chomp
end
describe 'gets' do
it 'belongs to Kernel' do
allow_any_instance_of(Kernel).to receive(:gets).and_return('class')
expect(run).to eq('class')
end
end
The method gets
actually belongs to the Kernel
module. (method(:gets).owner == Kernel
). Since Kernel
is included in Object
and almost all ruby objects inherit from Object
this will work.
Now if run
is an instance method scoped in a Class
I would recommend scoping the stubbing a bit more such that:
class Test
def run
puts "Enter 'class' to create a new class."
input = gets.chomp
end
end
describe 'gets' do
it 'can be stubbed lower than that' do
allow_any_instance_of(Test).to receive(:gets).and_return('class')
expect(Test.new.run).to eq('class')
end
# or even
it 'or even lower than that' do
cli = Test.new
allow(cli).to receive(:gets).and_return('class')
expect(cli.run).to eq('class')
end
end
Example
RSpec: Using a method stub to test interactive user input
You are receiving the error because you are passing the --format documentation
as a parameter to RSpec.
gets
reads the ARGV that have been passed. This also includes the RSpec parameters. You should use STDIN.gets
so that you only read standard input and not the parameters.
You can read more about in this question:
https://stackoverflow.com/a/19799223/6156030
RSpec asking for user input
Your suggestions should work actually. But there is another option.
orig_stdin = $stdin
$stdin = StringIO.new('exit')
assert(EchoApp.new.greeting,'Goodbye!')
# Or
expect(EchoApp.new.greeting).to eql('Goodbye!')
$stdin = orig_stdin
Let me know if this does not work.
Related Topics
The Program 'Rails' Is Currently Not Installed
Ruby Is Already Using the Class Name of My Model
Rails Cancan and State MAChine - Authorizing States
Insert Rows on Specific Line in a File
Project Euler #3 in Ruby Solution Times Out
Compass Error for Command 'Grunt Server'
Scraping an Angularjs Application
Linking Two Models in a Multi-Model Form
How to Refactor Openssl Pkcs5_Keyivgen in Ruby
Nesting Too Deep' Error While Retrieving JSON Using Httparty
Exclude Draft Articles from Solr Index with Sunspot
Comparing Identical Datetime Objects in Ruby -- Why Are These Two Datetime.Now's Not Equal
How to Mock an Instance Method of an Already Mocked Object
Using Ruby, What Is the Most Efficient Way to Get the Content Type of a Given Url