rspec fails even when variables should match - ruby

Methods in my class:
def get_class_info
#class_name = get_class_name
end
def get_class_name
puts "Enter the name for your class. Enter 'done' to return to the main prompt."
input = gets.chomp
if input.valid_class_name?
class_name = input.titleize
elsif input.downcase == "done"
run
else
puts "Invalid class name."
get_class_name
end
class_name
end
My test:
it "stores the name in the #class_name property" do
expect(cli.class_name).to eq('Song')
allow(cli).to receive(:gets) {'song'}
cli.get_class_info
end
It fails, saying it expected "Song" but got nil.
If I comment out the expect line and add a pry to the end of the test, querying cli.class_name returns "Song"!!!
Why is this test failing??
Update: Okay, moving the expect line to the bottom of the test apparently solved it. But I'm confused because usually the expectation goes before the actual method call. Am I wrong? What am I missing here?

Related

NoMethodError: undefined method `chomp' for nil:NilClass while running Ruby Rspec on gets.chomp

This is my first post here. I'm fairly new to Ruby, especially RSpec and have been running into an issue. I have written a method that uses gets.chomp to receive a player input. However I have this method called in another method
def prompt_move
loop do
#move = gets.chomp.to_i
return move if valid_move?(move)
puts "Invalid input. Enter a column number between 1 and 7"
end
end
def valid_move?(move)
#move.is_a?(Integer) && #move.between?(1, 7)
end
def play_round
print_board
prompt_player
#move = prompt_move
end
Here is the code for my RSpec tests:
describe ConnectFour do
subject(:game) { described_class.new }
let(:player){ double(Player) }
describe '#prompt_move' do
context 'move is a valid input' do
before do
allow(game).to receive(:gets).and_return('3\n')
end
it 'returns move and stops the loop' do
error_message = 'Invalid input. Enter a column number between 1 and 7'
expect(game).to_not receive(:puts).with(error_message)
game.prompt_move
end
end
context 'when given one invalid input, then a valid input' do
before do
letter = 'a'
valid_input = '1'
allow(game).to receive(:gets).and_return(letter, valid_input)
end
it 'completes loop and displays error message once' do
error_message = 'Invalid input. Enter a column number between 1 and 7'
expect(game).to receive(:puts).with(error_message).once
game.prompt_move
end
end
end
If I remove the #prompt_move method from #play_round the tests pass without any issue. However when I try to call it from within #play_round it gives me
NoMethodError:
undefined method `chomp' for nil:NilClass
I have been struggling to figure out what is causing this error so any suggestions would be greatly appreciated!
You're executing code in your class file.
new_game = ConnectFour.new
new_game.play_game
This will run every time you load the file, like when you're testing it. It will prompt for input and run gets. What it's getting is the code of the test file (for some rspec reason). That's invalid, so it keeps running gets until eventually there is no more input and gets returns nil.
Remove that from your class file. Code like that should be in a separate file which requires the class.

No Method Errors in Ruby

I was able to get a menu and pull up names of cat breeds, however when I continue to learn about the cat's breed I get this error down below. Not sure where to go from here. Am I suppose to delete something off? or perhaps try another api? really running out of ideas here.
Error below:
Traceback (most recent call last):
4: from bin/run.rb:5:in `<main>'
3: from /Users/jason/Development/code/Cat Breeds/Cat_breeds/lib/cli.rb:9:in `call'
2: from /Users/jason/Development/code/Cat Breeds/Cat_breeds/lib/cli.rb:24:in `menu'
1: from /Users/jason/Development/code/Cat Breeds/Cat_breeds/lib/cli.rb:44:in `list_of_breeds' /Users/jason/Development/code/Cat Breeds/Cat_breeds/lib/cli.rb:54:in `breed_selection': undefined method `get_metric_weight' for
#<CatBreed:0x00007fc581afc488> (NoMethodError)
in my cli.rb file
class CLI
def initialize
API.new.get_breed_data
end
def call
greeting
menu
end
def greeting
puts "Welcome! Start searching for Cat breeds"
puts ""
puts "--To search for cat breeds, enter 'breeds'"
puts ""
puts "--If there is nothing you would like to do at the moment, enter 'exit'"
end
def menu
input = gets.strip.downcase
if input == "breeds"
list_of_breeds
elsif input == "exit"
goodbye
else
invalid_entry
end
end
def goodbye
puts "Goodbye!"
end
def list_of_breeds
puts "Select which breed you would like to know about:"
CatBreed.all.each_with_index do |breed, index|
puts "#{index + 1}. #{breed.name}"
end
input = gets.strip.downcase
breed_selection(input)
end
def breed_selection(breed)
input = gets.strip.downcase
breed = CatBreed.find_by_name(breed)
if breed
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
puts "Name of Breed: #{breed.name}"
puts "Approximate Weight: #{breed.get_metric_weight}"
puts "Approximate Height: #{breed.get_metric_height}"
puts "Bred For: #{breed.bred_for}"
puts "Breed Group: #{breed.breed_group}"
puts "Average Life Span: #{breed.life_span}"
puts "Temperament: #{breed.temperament}"
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
puts ""
puts "To continue searching for breeds, enter 'breeds'."
puts "If there is nothing else you would like to do, enter 'exit'."
puts ""
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
menu
else
incorrect_breed_name
end
end
def incorrect_breed_name
puts "The breed name that you entered may have been spelled incorrectly."
puts "Please enter 'breeds' to pull up the list and try again."
menu
end
def invalid_entry
puts ""
puts "Hmmmmmm, I'm not understanding, please try again."
puts ""
menu
end
#binding.pry
end
something I'm doing wrong here? If you need more info let me know..
The error message says:
/Users/jason/Development/code/Cat Breeds/Cat_breeds/lib/cli.rb:54:in `breed_selection':
undefined method `get_metric_weight' for #<CatBreed:0x00007fc581afc488> (NoMethodError)
This error has got nothing to do with VSCode. It says that your CatBreed instance does not respond to a get_metric_weight method.
However, you have not shown the CatBreed class definition above, so I cannot comment further on the resolution.
Your code also assumes it has methods such as get_metric_height, bred_for and breed_group. If these methods aren't defined either, then I would expect a similar error to be raised once this issue is fixed.

Testing gets in rspec (user input)

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

Building proper class and passing a class method

class Health
def initialize()
#hydration=hydration
end
def hydration
puts"Amount of Water drunk(in oz):"
x=gets.chomp
if #{x}>=20
puts"Good job! keep on it"
elsif #{x} >=(10...20)
puts"Could do better"
else
puts"Not healthy. Go get hydration"
end
end
drinks=Health.new()
puts drinks.hydration
end
I am new to ruby and what i'm trying to achieve is to be prompted. i'm getting the "syntax error, unexpected end-of-input, expecting keyword_end" on the puts drinks.hydration
I dont think you should get any syntax error with your current code. In order to make it working you need to remove the comments in if statements. Also the initialize statement is redundant as you are making explicit call to the method hydration in puts drinks.hydration.
class Health
def hydration
puts "Amount of Water drunk(in oz):"
x=gets.chomp.to_i
if x>=20
puts "Good job! keep on it"
elsif (10...20).include? x
puts "Could do better"
else
puts "Not healthy. Go get hydration"
end
end
end
drinks=Health.new()
puts drinks.hydration

What is Camping::Server.start invoking in /bin/camping?

I'm studying how the Camping web framework works right now, and I don't understand what the Camping::Server.start at line 10 in /bin/camping is doing.
I expected this to call the start method in /lib/camping/server.rb at line 131, and so I put a simple puts 'hello' statement at the beginning of that method, expecting that statement to be invoked when I ran /bin/camping. However, I never saw my puts statement get called, so I can only assume that it's not that start method getting called.
I feel like I'm missing something obvious here. Here is the link to the camping github page and the relevant sections of code:
Github: https://github.com/camping/camping
From /bin/camping:
#!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'camping'
require 'camping/server'
begin
Camping::Server.start
rescue OptionParser::ParseError => ex
puts "did it error"
STDERR.puts "!! #{ex.message}"
puts "** use `#{File.basename($0)} --help` for more details..."
exit 1
end
From /lib/server.rb:
def start
if options[:server] == "console"
puts "** Starting console"
#reloader.reload!
r = #reloader
eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil }
ARGV.clear
IRB.start
exit
else
name = server.name[/\w+$/]
puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}"
super
end
end
My puts 'hello' on Camping::Server.start wasn't getting called because I didn't understand how static methods were defined in ruby.
start was being called statically, and I realize now that the start method I was looking at in the snippet wasn't a static method, which meant that another start method was getting called. I looked into Camping::Server and realized that it inherited from Rack::Server, which has the following method:
def self.start(options = nil)
new(options).start
end
That was the method getting called, not the one on /lib/camping/server.rb. I had been looking at the wrong method.

Resources