Testing gets in rspec (user input) - ruby

My class has this #run method that so far is just this, to test the testing:
def run
puts "Enter 'class' to create a new class."
input = $stdin.gets.chomp
binding.pry
And in the tests so far I've got
allow($stdin).to receive(:gets).and_return 'class'
cli.run
Doing it this way I am able to see, in the pry session, that input has been set to 'class', as intended.
Is there a way to do with without adding $stdin to my call to gets in my method itself? i.e., input = gets.chomp
I've tried allow(cli.run).to receive(:gets).and_return 'class'
But then in the pry session, input is equal to the first line of the spec file!

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

Related

Ruby with RSpec NoMethodError: undefined method length for nil:NilClass

I'm new to Ruby and RSpec trying to write a unit case for string length. I have 3 rb files as follows
1. Calling file
require_relative 'ruby_final_operations'
require_relative 'ruby_helper'
require 'uri'
require 'open-uri'
require 'prime'
module RubyOperations
# Public: Various commands for the user to interact with RubyCommand.
class Command
res = RubyOperations::Operations.new
res.letter_count(res.inputstr)
2nd File - Method Implementation
require_relative 'ruby_helper'
require 'logger'
$FILE_LOG = RubyOperations.create_log(File.expand_path('~/RubyOperations_LOG.log'), Logger::DEBUG)
$STD_LOG = RubyOperations.create_log(nil, Logger::INFO)
module RubyOperations
class Operations
def inputstr
RubyOperations.log('Enter the String:[Length 20]',:BOTH)
#str = gets.chomp
raise StandardError if #str =~ /\d/ || #str.empty? || #str.length > 20
rescue StandardError,ArgumentError => e
RubyOperations.log(e,:ERROR)
end
def letter_count(str)
result = #str.length
RubyOperations.log("The number of letters in the string: #{result}",:BOTH)
end
3rd file - RSpec
require 'ruby_final_operations'
describe 'RubyOperations' do
describe 'Operations' do
subject = RubyOperations::Operations.new
describe '.letter_count' do
context 'when operation is provided' do
it 'returns letter count' do
allow(subject.letter_count("hello").to receive(:result).and_return(5)
end
end
end
The problem is that in the 2nd File he argument is 'str' but the typed string is stored is '#str'.
How can I pass the string 'hello' from the rspec file to test this.
There are a few issues:
Calling a instance_method with an argument that is not used
def letter_count #get rid of argument, the argument does nothing,
#basically it looks you added the argument,
# just, so you can call the other method there.
Make your main simple, with a clear sequence
res.inputstr
res.letter_count
But about your actual question, in your test you change the wrong thing the wrong method
allow(subject.letter_count("hello").to receive(:result).and_return(5)
# letter count should do the log entry, not return five, at least that what your method say
So you probably want to set the #str before you test the letter_count method.
subject.instance_variable_set("hello")
# then test for what you expect the method to return
expect(subject.letter_count).to eq(5)
# this specific test will fail, because you are doing a log entry, and not return the length on letter_count.

RSpec: simulating user input (via gets) without the test prompting for it

I'm coding a game by taking a TDD first approach, and have gotten stuck because the test keeps stopping for user input (repo is here).
I want the test to simulate user input rather than prompting for it, as I've set up some let keywords and have tried to account for user input that comes in via gets.chomp.
Here is where the game prompts for user input:
game.rb
module ConnectFour
class Game
def start_game
puts 'Welcome to Connect Four.'
puts "Enter name of player 1 (red)"
player1name = gets.chomp
player1 = Player.new(player1name)
end
end
end
And here is the test code:
game_spec.rb
require 'spec_helper'
module ConnectFour
describe Game do
let(:game) { Game.new }
let(:player1name) { 'Bob' }
let(:player1) { Player.new(player1name) }
describe 'Instantiate game play objects' do
describe 'Create player 1' do
it 'Provide player 1 name' do
allow_any_instance_of(Kernel)
.to receive(:gets)
.and_return(player1name)
end
it 'Instantiate player 1' do
expect(player1.name).to eq player1name
end
end
end # describe 'Instantiate game play objects'
end # Describe 'Game'
end
So far I've tried encapsulating the gets.chomp in its own method as recommended here but this has no effect. I've also tried prefixing $stdin to gets.chomp statements in the Ruby code but yeah, that was pretty useless. I had asked a similar question here recently and thought I had understood how to simulate user input but obviously not... any help would be appreciated.
use allow_any_instance_of(Object) instead of Kernel. The module Kernel is included into Object. Kernel is not ever actually instantiated because it's a module.
kind of a small point, but it'd be more accurate if you stubbed gets to return a strinng ending in \n, otherwise you could remove the chomp from the tested functionn and the test will still pass
reproducable example
require 'rspec'
require 'rspec/expectations'
test_case = RSpec.describe "" do
it "" do
allow_any_instance_of(Object).to receive(:gets).and_return "something\n"
puts gets.chomp
end
end
test_case.run

Using RSpec to test user input with gets

I'm new to Unit Testing using RSpec and Ruby and I have a question on how to test if my code is using the gets method, but without prompting for user input.
Here is the code I'm trying to test. Nothing crazy here, just a simple one liner.
my_file.rb
My_name = gets
Here's my spec.
require 'stringio'
def capture_name
$stdin.gets.chomp
end
describe 'capture_name' do
before do
$stdin = StringIO.new("John Doe\n")
end
after do
$stdin = STDIN
end
it "should be 'John Doe'" do
expect(capture_name).to be == 'John Doe'
require_relative 'my_file.rb'
end
end
Now this spec works, but when I run the code it prompts for user input. I don't want it to do that. I want to simply test if the gets method is being called and possibly mock the user input. Not to sure how to achieve this in RSpec. In Python I would utilize unittest.mock is there a similar method in RSpec?
Thanks in advance!
Here's how you could stub gets with your return value.
require 'rspec'
RSpec.describe do
describe 'capture_name' do
it 'returns foo as input' do
allow($stdin).to receive(:gets).and_return('foo')
name = $stdin.gets
expect(name).to eq('food')
end
end
end
Failures:
1) should eq "food"
Failure/Error: expect(name).to eq('food')
expected: "food"
got: "foo"
(compared using ==)
To test if something is being called (such as a function) you can use expect($stdin).to receive(:gets).with('foo') to ensure it is being called (once) with the right args. The expectation line in this scenario has to go before name = $stdin.gets.

My very simple custom Puppet type and provider does not work

I am reading about how to create custom types and providers in Puppet.
But I am getting the error:
Error: Could not autoload puppet/provider/createfile/ruby: undefined method `[]' for nil:NilClass
when running the below code:
mymodule/lib/puppet/type/filecreate.rb
require 'fileutils'
Puppet::Type.newtype(:filecreate) do
ensurable do
defaultvalues
defaultto :present
end
#doc = "Create a file."
newproperty(:name, :namevar => true) do
desc "The name of the file"
end
newproperty(:path) do
desc "The path of the file"
end
end
mymodule/lib/puppet/provider/filecreate/ruby.rb
require 'fileutils'
Puppet::Type.type(:filecreate).provide(:ruby) do
desc "create file.."
puts resource[:name] # this line does not seem to work, why?
puts resource[:path] # this line does not seem to work, why?
def create
puts "create file..."
puts resource[:name]
end
def destroy
puts ("destroy file...")
FileUtils.rm resource[:path]+resource[:name]
end
# Exit method never seems to be called
def exists?
puts "is method beeing called???"
File.exists?(resource[:path])
end
end
I guess the way of fetching the parameter values, puts resource[:name] not is correct. So how can I fetch the filename file.txt declared as the namevar for my custom type filecreate (see below)?
Also, method exists does not seem to be called. Why?
And my init.pp contains this simple code:
class myclass {
filecreate{'file.txt':
ensure => present,
path => '/home/myuser/',
}
}
Your puts calls do not work because you try and access an instance attribute (resource) on the class level. It makes no semantic sense to access the values in this context. Remove those calls.
Generally, it is better to use Puppet.debug instead of puts to collect this kind of information.
To find out where such errors come from, call puppet with the --trace option.

Accessing/Dealing with Variables in Ruby

Let me preface by stating I'm a "new" programmer - an IT guy trying his hand at his first "real" problem after working through various tutorials.
So - here is what I'm trying to do. I'm watching a directory for a .csv file - it will be in this format: 999999_888_filename.csv
I want to return each part of the "_" filename as a variable to pass on to another program/script for some other task. I have come up w/ the following code:
require 'rubygems'
require 'fssm'
class Watcher
def start
monitor = FSSM::Monitor.new(:directories => true)
monitor.path('/data/testing/uploads') do |path|
path.update do |base, relative, ftype|
output(relative)
end
path.create do |base, relative, ftype|
output(relative)
end
path.delete { |base, relative, ftype| puts "DELETED #{relative} (#{ftype})" }
end
monitor.run
end
def output(relative)
puts "#{relative} added"
values = relative.split('_',)
sitenum = values[0]
numrecs = values[1]
filename = values[2]
puts sitenum
end
end
My first "puts" gives me the full filename (it's just there to show me the script is working), and the second puts returns the 'sitenum'. I want to be able to access this "outside" of this output method. I have this file (named watcher.rb) in a libs/ folder and I have a second file in the project root called 'monitor.rb' which contains simply:
require './lib/watcher'
watcher = Watcher.new
watcher.start
And I can't figure out how to access my 'sitenum', 'numrecs' and 'filename' from this file. I'm not sure if it needs to be a variable, instance variable or what. I've played around w/ attr_accessible and other things, and nothing works. I decided to ask here since I've been spinning my wheels for a couple of things, and I'm starting to confuse myself by searching on my own.
Thanks in advance for any help or advice you may have.
At the top of the Watcher class, you're going to want to define three attr_accessor declarations, which give the behavior you want. (attr_reader if you're only reading, attr_writer if you're only writing, attr_accessor if both.)
class Watcher
attr_accessor :sitenum, :numrecs, :filename
...
# later on, use # for class variables
...
#sitenum = 5
...
end
Now you should have no problem with watcher.sitenum etc. Here's an example.
EDIT: Some typos.
In addition to Jordan Scales' answer, these variable should initialized
class Watcher
attr_accessor :sitenum, :numrecs, :filename
def initialize
#sitenum = 'default value'
#numrecs = 'default value'
#filename = 'default value'
end
...
end
Otherwise you'll get uninformative value nil

Resources