Rspec: No such file or directory @ rb_sysopen -

Viewed 1865

I'm trying to write a simple test for a ruby script that uses gets and I'm experiencing some weird behavior.

When I run the test with rspec it passes fine, but as soon as I run rspec with any flags, like rspec -f json or rspec -O rand the test will fail and give me No such file or directory @ rb_sysopen - -O, where -O is whatever the flag that was run.

Stranger still is that if I run rspec and specify the test file like rspec spec/gets_spec.rb, I get a completely different error:

     Failure/Error: expect { require_relative '../gets' }.to output("Hello, Lena!\n").to_stdout                         
       expected block to output "Hello, Lena!\n" to stdout, but output "Hello, Describe \"gets.rb\" do!\n"
                     Diff:                                                                                                              
@@ -1,2 +1,2 @@                                                                                                         
-Hello, Lena!                                                                                                           
+Hello, Describe "gets.rb" do!                                                                                                                                                                                                                
# ./spec/gets_spec.rb:14:in `block (2 levels) in <top (required)>' 

I'm having trouble figuring out the right way to write my test so that this doesn't happen but I'm not sure what part of my code needs to change.

I'm using ruby 2.6.3

I learned about testing input from this question

The script, gets.rb, looks like

puts "Hello, #{gets.chomp.capitalize}!"

My test looks like

describe "gets.rb" do
  before do
      $stdin = StringIO.new("lena")
  end

  after do 
      $stdin = STDIN
  end

  it "should output 'Hello, name!'" , points: 1 do
    allow($stdin).to receive(:gets).and_return("lena")

    expect { require_relative "../gets" }.to output("Hello, Lena!\n").to_stdout
  end
end

Here's the full failure message:

Failures:

  1) gets.rb should output 'Hello, name!'
     Failure/Error: expect { require_relative '../gets' }.to output("Hello, Lena!\n").to_stdout                        
     Errno::ENOENT: 
No such file or directory @ rb_sysopen - -O                                                                          
#./gets.rb:1:in `gets'                     
# ./gets.rb:1:in `gets' 
# ./gets.rb:1:in `<top (required)>' 
# ./spec/gets_spec.rb:14:in `require_relative' 
# ./spec/gets_spec.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/gets_spec.rb:14:in `block (2 levels) in <top (required)>'  
1 Answers

You can try to use a little different approach:

describe "gets.rb" do
  it "should output 'Hello, name!'" do
    allow_any_instance_of(Object).to receive(:gets).and_return("lena")

    expect { require_relative "../gets.rb" }.to output("Hello, Lena!\n").to_stdout
  end
end

allow_any_instance_of I took from https://makandracards.com/makandra/41096-stubbing-terminal-user-input-in-rspec

Reading user input in console applications is usually done using Kernel#gets.

The page suggests to use Object.any_instance.stub(gets: 'user input') but it's deprecated syntax already and I've updated it.

Related