Basic RSpec issue - method runs but tests fail - ruby-on-rails

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.

Related

Simulating a Timeout in RSpec?

I’m writing regression tests for a part of a Rails application that looks like this:
Timeout.timeout(mode == :index ? 6 : 45) do
imap = Net::IMAP.new(...)
end
I want to verify that in each case (i.e., mode == :index & mode != :index), the correct Timeout length is set.
I tried using ActiveSupport's travel:
allow(Net::IMAP).to receive(:new).and_wrap_original do |m, *args|
travel 6
m.call(*args)
end
expect { method }.to raise_error(Timeout::Error)
but it doesn’t trigger the Timeout. If I use sleep instead then it does work, but obviously, I’m not putting sleep 45 in a unit test.
How can I simulate the passage of time to trigger a Timeout in a test?

Expecting string but getting nil in Rspec test

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

RSpec - expected equal?( Time Object ) to return true, got false

I'm trying (and failing!) the following code to compare two Time objects in RSpec:
describe '#current_shift_datetime' do
subject { WipStack.current_shift_datetime }
it "To be a Time object" do
expect(subject).to be_kind_of(Time)
end
it "To return current shift datetime" do
# Trying to figure out why they are not equal
puts subject.in_time_zone.round
puts 0.day.ago.midnight.in_time_zone.round
# Problematic code here -->
expect(subject.in_time_zone.round).to be_equal(0.day.ago.midnight.in_time_zone.round)
# End of problematic code
end
end
I have read a couple of things in the internet about rspec time comparisong, one of them explaining the problem with the milliseconds (therefore the round) and another page talking about stub but then i ended with An error occurred in an after hook SystemStackError: stack level too deep.
The ouput of the test is:
1) WipStack#current_shift_datetime To return current shift datetime
Failure/Error: expect(subject.in_time_zone.round).to be_equal(0.day.ago.midnight.in_time_zone.round)
expected equal?(Mon, 17 Feb 2014 00:00:00 CST -06:00) to return true, got false
The puts output:
#current_shift_datetime
To be a Time object
2014-02-17 00:00:00 -0600
2014-02-17 00:00:00 -0600
To return current shift datetime (FAILED - 1)
Update:
Here is the current_shift_datetime method:
def WipStack.current_shift_datetime(scope = nil)
shifts = WipStack.get_shifts(scope)
current_shift = (Time.zone.now - 1.day).strftime("%m/%d/%Y")+" "+shifts.last
current_time = Time.zone.now.strftime("%H:%M")
shifts.each do |shift|
if current_time > shift
current_shift = Time.zone.now.strftime("%m/%d/%Y")+" "+shift
end
end
Time.zone.parse(current_shift)
end
Rather than compare the time zones directly (and get bogged down in subtle differences), you could try comparing the string representations to each other, since that's what you're checking with in the console anyhow.
Depending on how you use this timezone in your app, this should be sufficient to say they are essentially the same time.
expect(subject.in_time_zone.round.to_s).to eq(0.day.ago.midnight.in_time_zone.round.to_s)
You could probably drop the .round if you wanted to as well.
EDIT
Try changing your code so there is no difference between what is printed and what is compared:
it "To return current shift datetime" do
a = subject.in_time_zone.round.to_s
b = 0.day.ago.midnight.in_time_zone.round.to_s
puts a
puts b
expect(a).to eq(b)
end
I fixed it thanks to tyler! It seems that be_equal was actually trying to see if both objects were the same (a comment appear on the test failing when comparing the strings), changing be_equal to eq made the test pass!
it "To return current shift datetime" do
#pending
puts subject.in_time_zone.round.to_s
puts 0.day.ago.midnight.in_time_zone.round.to_s
expect(subject.in_time_zone.round.to_s).to eq(0.day.ago.midnight.in_time_zone.round.to_s)
end

How to test the eq of a .rjust 8, "0" with rspec

I have a little function to make a customer_nr with
id.to_s.rjust 8, "0"
This gives me a 8 diget number with the id and before filled with 0 until I have 8 diggets.
How can I test this with rspec? The first test works, but the second on is wrong since the id is interactive. Any Idea how to test the eq? FactoryGirl creates a user with FactoryGirl.create(:user). This works if the created user gets an single digget id.
When it gets a more diggets id then the test fails.
Like expected: "00000002398" got: "00002398"
describe "#customer_nr" do
it "should have 8 digits" do
user.customer_nr.length.should eq 8
end
it "should use the user_id" do
user.customer_nr.should eq "0000000#{user.id}"
end
end
best regard
denym
After my comment and your edit issue is easy. Everything is correct but your user have id 2398. You can make test in irb
2398.to_s.rjust 8, "0"
give exactly what test return. In test your db created records and destroy after tested but id still increment. You can make:
user.customer_nr.should eq "0000000#{user.id}".split(//).last(8).join
or like toro2k wrote in his comment.

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