Expecting string but getting nil in Rspec test - ruby-on-rails

I am working on a Ruby problem called "Speaking Grandma" where I need to create a method that should should take in a string argument containing a phrase and check to see if the phrase is written in all uppercase: if it isn't, then grandma can't hear you. She should then respond with (return) HUH?! SPEAK UP, SONNY!.
However, if you shout at her (i.e. call the method with a string argument containing a phrase that is all uppercase, then she can hear you (or at least she thinks that she can) and should respond with (return) NO, NOT SINCE 1938!.
I wrote the following code:
def speak_to_grandma(input)
if
input != input.upcase
puts 'HUH?! SPEAK UP, SONNY!'
else
puts 'NO, NOT SINCE 1938!'
end
end
When I run RSpec, I fail both tests. It gives the following message:
Failure/Error: expect(speak_to_grandma('Hi Nana, how are you?')).to eq 'HUH?! SPEAK UP, SONNY!'
expected: "HUH?! SPEAK UP, SONNY!"
got: nil
and
Failure/Error: expect(speak_to_grandma('WHAT DID YOU EAT TODAY?')).to eq "NO, NOT SINCE 1938!"
expected: "NO, NOT SINCE 1938!"
got: nil
(compared using ==)
I have no idea what I am doing wrong here. Can anyone help?

The speak_to_grandma method returns the return value of the puts method, being the last line in the method. The return value of puts is nil(Why are all my puts returning =>nil?)
The eq method in Rspec checks the return value of a method. The string is output to the screen with puts, but the return value is nil, and that's what Rspec is checking for.
If you remove puts, the tests should pass, because the string will be the return value of the method. But the correct way would be to test it with the output method in Rspec. Write your test like this:
expect { speak_to_grandma('WHAT DID YOU EAT TODAY?') }.to output("NO, NOT SINCE 1938!").to_stdout

Related

RSpec do not see updated attributes in factory_bot object

I have report object called SiegeReport, that makes some calculations and returns integer object. When there is no warrior in the building, then siege_ability equals 0. The code itself is not important here, because it works fine in console and in application. Factories made by factory_bot work ok in all the other examples. I just have problem with testing the method:
require 'rails_helper'
RSpec.describe Reports::SiegeReport do
subject(:siege_report) { Reports::SiegeReport.new(building: building).call }
let(:building) { create(:building, granary: 100) }
let(:clan) { create(:clan) }
context 'with 1 infantry' do
let(:warrior) { create(:warrior, clan_id: clan.id, building_id: building.id) }
it 'returns 9' do
expect(siege_report).to eq(9)
end
end
end
RSpec returns:
Reports::SiegeReport siege ability with 1 infantry returns 9
Failure/Error: expect(siege_report).to eq(9)
expected: 9
got: 0
(compared using ==)
I checked it with pry and warrior object is valid, even building.warriors returns warrior, but in attributes number of warriors is still 0. The very same example works when i type it manually in console. How can I make RSpec update building attributes before testing?
Ok, I found solution but is really not elegant:
it 'returns 9' do
warrior.save
expect(siege_report).to eq(9)
end
It works but it seems to me that it is not the proper way of testing. Still, I can't find a better solution.

How to return a value when using execute_script in capybara?

I have a really simple line in a test that calls execute script like this:
puts page.execute_script("return somefunction();").to_i.inspect
And in javascript I have a function like this:
function somefunction(){
console.log("test");
return 999;
}
The 'test' from the console log is getting logged out so it is running however...
Looking at the logs when running the test, the execute_script returns 0 not 999, so in rspec I can't get at the return value from the function, how do I make page.execute_script return that value from that function?
The Poltergeist driver is designed to return nil for execute_script:
def execute_script(script)
browser.execute(script)
nil
end
Poltergeist will only return a value if you use the evaluate_script:
def evaluate_script(script)
browser.evaluate(script)
end
Capybara has corresponding methods for each - ie Session#execute_script and Session#evaluate_script. Your code should work if you switch to using evaluate_script (and as #AndreyBotalov points out, you also need to remove the return):
puts page.evaluate_script("somefunction();").to_i.inspect

Basic RSpec issue - method runs but tests fail

Given all the hype over TDD, I decided it was time to dig in and add that to the list of things to study. I'm running into an issue, and I'm 100% certain it's just a function of something being wrong with my tests in RSpec. I'm still brand new to RSpec, so I'm having trouble figuring it out... my method works just fine, but the test for the method does not.
Method Code (I know I can refactor this A LOT. This is one of the first Ruby programs I wrote awhile back, which explains the ugliness)
def caesar_cipher(string,offset)
string=string.to_s
offset=offset.to_i
cipher=[]
string.each_byte do |i|
#capital letters
if (i>64 && i<91)
if (i+offset)>90
cipher << (i+offset-26).chr
else
cipher << (i+offset).chr
end
elsif (i>96 && i<123)
if (i+offset)>122
cipher << (i+offset-26).chr
else
cipher << (i+offset).chr
end
else
cipher << i.chr
end
end
cipher=cipher.join('')
puts "The encrypted string is: #{cipher}"
end
puts "Enter the string you'd like to encrypt"
string=gets.chomp
puts "Enter the offset you'd like to use"
offset=gets.chomp
caesar_cipher(string,offset)
Test Code (Just for one generic case, all lower case input)
require './caesarCipher.rb'
describe "caesar_cipher" do
it 'should handle all lower case input' do
caesar_cipher("abcdefg", 3).should == "defghij"
end
end
Method output:
$ ruby caesarCipher.rb
Enter the string you'd like to encrypt
abcdefg
Enter the offset you'd like to use
3
The encrypted string is: defghij
Test Output:
$ rspec spec/caesar_cipher_spec.rb
Enter the string you'd like to encrypt
Enter the offset you'd like to use
The encrypted string is: require './caesarCipher.rb'
The encrypted string is: defghij
F
Failures:
1) caesar_cipher should handle all lower case input
Failure/Error: caesar_cipher("abcdefg", 3).should == "defghij"
expected: "defghij"
got: nil (using ==)
# ./spec/caesar_cipher_spec.rb:5:in `block (2 levels) in <top (required)>'
Finished in 0.00542 seconds (files took 0.14863 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/caesar_cipher_spec.rb:4 # caesar_cipher should handle all lower case input
Any help on why the tests are failing? Judging by the output it looks like it's running it twice or something in the tests.
Add cipher or return cipher after this line
puts "The encrypted string is: #{cipher}"
And it should work
To explain the fix given, the last expression in a method is the return value. You've passed the value to STDOUT but not as the return value of the method, so RSpec was failing.

Class methods and expect argument

I have simple test case: (board has_many links, link belongs_to board)
context "with feeds" do
let (:board) {FactoryGirl.create(:board_tree)}
it "returns links from all feeds" do
expect(board.all_links.count).to eq Link.all.count
end
end
It fails (expected 8 got 2) - which is ok, I expected the same.
Now I switch this expect with:
expect(Link.all.count).to eq board.all_links.count
This time I thought it will fail with expected 2 got 8, but instead of it I get: expected 2, got 0.
Any idea why? For me both expectations should be equal.
If you try changing:
let (:board) {FactoryGirl.create(:board_tree)}
into
let! (:board) {FactoryGirl.create(:board_tree)}
you will see that the results won't change when you invert the conditions. The problem is that links are created by rspec the first time that the board variable is referenced, so in the second example, there are no links yet when rspec is evaluating the expect(Link.all.count) part.

Having test coverage problems with Rails instance method in model with Stripe

I'm using the Simplecov gem to output my test coverage and for an odd reason can not get 2 lines in my Rails instance method in my model to be captured. Also, any insight into why Simplecov states that the entire method is covered except for the 2 lines when I have not even included a describe block within my specs would be great as well. Any help would be greatly appreciated.
def process
if valid?
payment = Stripe::Charge.create(amount: amount, currency: "usd",
card: stripe_card, capture: false)
if (payment[:card][:address_line1_check] &&
payment[:card][:cvc_check] &&
payment[:card][:address_zip_check]).eql?("pass")
charge = Stripe::Charge.retrieve(payment[:id]) # This line is not captured
charge.capture # This line is not captured
end
self.transaction_number = payment.id
save!
end
end
Simplecov is showing you two things:
At least once during the test run, process is called.
At no point during the test run does the condition of the if statement evaluate to a truthy value; consequently, the body of the statement is never reached.
Simplecov doesn't care whether you explicitly created a describe block: Simplecov simply looks at which statements were executed during the test run.
Separately, I don't think the logic of your if condition does what you expect (and using eql? is not very idiomatic).
if (payment[:card][:address_line1_check] &&
payment[:card][:cvc_check] &&
payment[:card][:address_zip_check]).eql?("pass")
Each of these values can be one of {nil, "pass", "fail", "unchecked"}. A string value is truthy: "a" && "b" == "b" but nil && "b" == nil. Your code could be executed even if address_line1_check were "fail".
If you want to test that all three values are equal to "pass", this will do it:
if [payment[:card][:address_line1_check],
payment[:card][:cvc_check],
payment[:card][:address_zip_check]].all? { |v| v == "pass" }

Resources