using assert_raise within ruby testing - ruby

I'm creating a testing file for a ruby program that finds factorials of given numbers. All of my tests run well except I get an error with my string and negative number tests that should raise exceptions. I'm not entirely sure of the syntax for raise exception, I've read the docs.
this is my code for the factorial program itself, n is the number thats supposed to be passed:
if n.is_a?(Integer) == false
raise 'strings are not acceptable'
end
if n < 0
raise 'negatives are not acceptable'
end
the test case in my test file are as follows:
def test_negative
factorial(-1)
assert_raise do
end
end
def test_string
factorial('hello')
assert_raise do
end
end
both of my tests come back as errors while my other 3, that test normal numbers, come back as passed. I'm new to ruby but I would just want a pass after assert_raise do as my actual error message is in my factorial program right?

in your first test case, it will not return an error because -1 is also considered as an Integer
#irb output
2.0.0-p247 :003 > a = -1
=> -1
2.0.0-p247 :004 > a.is_a?(Integer)
=> true
and in your second case, when you pass a string, it will error even before going inside your condition as you are trying to compare string with an integer
#irb outout
2.0.0-p247 :007 > "hello" < 0
ArgumentError: comparison of String with 0 failed
from (irb):7:in `<'
from (irb):7
and off topic, you could write
if n.is_a?(Integer) == false
raise 'strings are not acceptable'
end
as (more ruby way :))
raise 'strings are not acceptable' unless n.is_a?(Integer)

Related

Codility error: Invalid result type, Integer expected, NilClass found

EDIT: SOLVED BY STEFAN
BUT: Now, the only questions left are: Why has the shorter solution such a poor performance (results: 100%, performance: 32%, result: 66%) , while the longer version performs a bit better but seems to produce worse results (60%, 50%, 55%)?
Start of original question:
I'm currently trying the Codility demo test and the problem to solve is to find the lowest integer above 0 that's not included in a given array.
This is my code in two different versions with the same result. The output is right but the compiler throws the abovementioned error, resulting in the test failing. This seems to be a common error on Codility, when looking up this error here on SO.
# you can write to stdout for debugging purposes, e.g.
# puts "this is a debug message"
def solution(a)
# write your code in Ruby 2.2
num = 1
a=a.sort
a.uniq!
a.each do |x|
if x == num then
num += 1
next
else
break
end
end
puts num
end
or
def solution(a)
# write your code in Ruby 2.2
num = 1
while a.include?(num) do
num += 1
end
puts num
end
results in:
Compilation successful.
Example test: [1, 3, 6, 4, 1, 2]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
5
RUNTIME ERROR (tested program terminated with exit code 1)
Example test: [1, 2, 3]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
4
RUNTIME ERROR (tested program terminated with exit code 1)
Example test: [-1, -3]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
1
RUNTIME ERROR (tested program terminated with exit code 1)
Producing output might cause your solution to fail performance tests.
You should remove code that produces output before you submit your solution.
Detected some errors.
I really don't understand what's wrong. The array only contains integers, num is an integer, everything is an integer but the compiler says it's NIL. What can I do?
EDIT: The same code runs without errors in the SoloLearn app and on my local machine.
The output is right but the compiler throws the abovementioned error, resulting in the test failing
Although puts generates output it has a return value of nil:
puts 123
# 123 # <- output
#=> nil # <- return value
I assume that your method is supposed to return the value instead just printing it to standard out.
You can fix this by removing puts in your method's last line:
def solution(a)
num = 1
while a.include?(num)
num += 1
end
num # <- without "puts"
end
To generate debug output you could add puts num on a separate line before the return value, e.g.:
def solution(a)
# ...
puts num # <- prints num
num # <- returns num
end
or you could use p which outputs the object's inspect value and returns the object:
def solution(a)
# ...
p num # <- prints num.inspect and returns num
end
Regarding the performance: try to understand what the code has to do in order to get the result. The "short" solution increments num and checks whether it is included in the array. But an inclusion check has to traverse the array (at least up to the matching element). So for each increment of num, you are traversing the array from the beginning.
You can speed this up significantly by utilizing Set for the lookup:
require 'set'
def solution(a)
set = Set.new(a)
num = 1
num += 1 while set.include?(num)
num
end
def solution(a)
range = (1..a.max).to_a
(range - a).first
end
If you are printing within the code just remove that lines
for Ex in Javascript remove used console.log

Ruby not allowing me to compare values

I made the code below, but when I run it, including if I pick the correct number, the console prints the debug. Why is this? What am I doing wrong?
puts 'Welcome to the number guessing game. I will pick a number between 1-100. It will be your job to guess the number. If you are incorrect, I will tell you if your guess is higher or lower than my number, I will let you know.'
puts "Time to guess!"
mine = (rand(1..100))
puts mine
grabber = gets.chomp!
if mine == grabber
puts 'That\'s it!'
else
print 'debug'
end
You're comparing values of two different types. gets returns a string, rand(1..100) returns an integer. You can't compare them directly. You need to convert them to the same type, either both integer or both string.
Try using to_i on the string to convert it to an integer:
if mine == grabber.to_i
As #meagar said, you are comparing different types (a string from gets and a number from rand) which will always returning false.
That being said, you have a couple of different ways to coerce/convert datatypes in ruby.
The most common one, as #maeger showed, is using to_i, however it can lead to some strange behaviours as any string that isn't easily parsed as an integer will return 0.
2.5.3 :001 > 'potato'.to_i
=> 0
2.5.3 :002 > '0xff'.to_i
=> 0
If you want to avoid this you can use Integer(arg), this is actually a method defined in Kernel that will do its best to verify if the string is actually convertible into an integer and if it fails it will raise an ArgumentError.
2.5.3 :001 > Integer('potato')
=> ArgumentError (invalid value for Integer(): 'potato')
2.5.3 :002 > Integer('2')
=> 2
2.5.3 :003 > Integer('0xff') # Hexadecimal
=> 255
2.5.3 :004 > Integer('0666') # Octal
=> 438
2.5.3 :005 > Integer('0b1110') # Binary
=> 14

Bad value for range

When I run the following code:
def db(y)
return self % y == 0
end
puts "To number:"
n = gets.chomp
for i in 1..n
if i.db(3)
puts "Fizz!"
if i.db(5)
puts "FIZZBUZZ!"
end
elsif i.db(5)
puts "Buzz!"
else
puts i
end
end
I get a "bad value for range" error. Why does this happen how do I fix it? Normal ranges that use variables for some values work perfectly including for loops, why does this not work?
Note: I want the for loop to stay as a for loop.
Just do as below :
n = gets.chomp.to_i
gets.chomp will give you String instance. You need to make it as Fixnum. Otherwise 1.."4" for e.g is not a valid range. so error "bad value for range" error. String#to_i is your friend.
2.0.0p0 :001 > 1.."2"
ArgumentError: bad value for range
from (irb):1
from /home/kirti/.rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'
2.0.0p0 :002 > 1..2
=> 1..2
2.0.0p0 :003 >
gets returns String.
You need to convert it to Fixnum using String#to_i.
Replace the following line:
n = gets.chomp
With:
n = gets.chomp.to_i

Why do I get different results in IRB and a script?

Is there a difference in how IRB and Ruby execute some expressions?
These expressions give different results in IRB and when run from the command line. The question is, which one is correct?
IRB:
>> s = 'hello'
=> "hello"
>> s.size
>> s[s.length] = '!'
IndexError: index 5 out of string
from (irb):31:in `[]='
from (irb):31
>>
And in the normal script:
s = 'hello'
s[s.length] = '!'
puts s
laptop user$ ./prgruby.rb
hello!
Here is the doc of String#[] for 1.8.7 :
str[fixnum] = fixnum
The forms that take a Fixnum will raise an IndexError if the value is
out of range
Here is the same doc for 1.9.3 : the same definition is present
After test, what happen in Ruby 1.9.3 is s.length is not out of range for assignation. This make sense at it is the end of the string : you do not have to arbitrary fill the missing indexes but I guess it may be or should be documented somewhere ?

Safe integer parsing in Ruby

I have a string, say '123', and I want to convert it to the integer 123.
I know you can simply do some_string.to_i, but that converts 'lolipops' to 0, which is not the effect I have in mind. I want it to blow up in my face when I try to convert something invalid, with a nice and painful Exception. Otherwise, I can't distinguish between a valid 0 and something that just isn't a number at all.
EDIT: I was looking for the standard way of doing it, without regex trickery.
Ruby has this functionality built in:
Integer('1001') # => 1001
Integer('1001 nights')
# ArgumentError: invalid value for Integer: "1001 nights"
As noted in answer by Joseph Pecoraro, you might want to watch for strings that are valid non-decimal numbers, such as those starting with 0x for hex and 0b for binary, and potentially more tricky numbers starting with zero that will be parsed as octal.
Ruby 1.9.2 added optional second argument for radix so above issue can be avoided:
Integer('23') # => 23
Integer('0x23') # => 35
Integer('023') # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10) # => 23
This might work:
i.to_i if i.match(/^\d+$/)
Also be aware of the affects that the current accepted solution may have on parsing hex, octal, and binary numbers:
>> Integer('0x15')
# => 21
>> Integer('0b10')
# => 2
>> Integer('077')
# => 63
In Ruby numbers that start with 0x or 0X are hex, 0b or 0B are binary, and just 0 are octal. If this is not the desired behavior you may want to combine that with some of the other solutions that check if the string matches a pattern first. Like the /\d+/ regular expressions, etc.
Another unexpected behavior with the accepted solution (with 1.8, 1.9 is ok):
>> Integer(:foobar)
=> 26017
>> Integer(:yikes)
=> 26025
so if you're not sure what is being passed in, make sure you add a .to_s.
I like Myron's answer but it suffers from the Ruby disease of "I no longer use Java/C# so I'm never going to use inheritance again". Opening any class can be fraught with danger and should be used sparingly, especially when it's part of Ruby's core library. I'm not saying don't ever use it, but it's usually easy to avoid and that there are better options available, e.g.
class IntegerInString < String
def initialize( s )
fail ArgumentError, "The string '#{s}' is not an integer in a string, it's just a string." unless s =~ /^\-?[0-9]+$/
super
end
end
Then when you wish to use a string that could be a number it's clear what you're doing and you don't clobber any core class, e.g.
n = IntegerInString.new "2"
n.to_i
# => 2
IntegerInString.new "blob"
ArgumentError: The string 'blob' is not an integer in a string, it's just a string.
You can add all sorts of other checks in the initialize, like checking for binary numbers etc. The main thing though, is that Ruby is for people and being for people means clarity. Naming an object via its variable name and its class name makes things much clearer.
I had to deal with this in my last project, and my implementation was similar, but a bit different:
class NotAnIntError < StandardError
end
class String
def is_int?
self =~ /^-?[0-9]+$/
end
def safe_to_i
return self.to_i if is_int?
raise NotAnIntError, "The string '#{self}' is not a valid integer.", caller
end
end
class Integer
def safe_to_i
return self
end
end
class StringExtensions < Test::Unit::TestCase
def test_is_int
assert "98234".is_int?
assert "-2342".is_int?
assert "02342".is_int?
assert !"+342".is_int?
assert !"3-42".is_int?
assert !"342.234".is_int?
assert !"a342".is_int?
assert !"342a".is_int?
end
def test_safe_to_i
assert 234234 == 234234.safe_to_i
assert 237 == "237".safe_to_i
begin
"a word".safe_to_i
fail 'safe_to_i did not raise the expected error.'
rescue NotAnIntError
# this is what we expect..
end
end
end
someString = "asdfasd123"
number = someString.to_i
if someString != number.to_s
puts "oops, this isn't a number"
end
Probably not the cleanest way to do it, but should work.
Re: Chris's answer
Your implementation let's things like "1a" or "b2" through. How about this instead:
def safeParse2(strToParse)
if strToParse =~ /\A\d+\Z/
strToParse.to_i
else
raise Exception
end
end
["100", "1a", "b2", "t"].each do |number|
begin
puts safeParse2(number)
rescue Exception
puts "#{number} is invalid"
end
end
This outputs:
100
1a is invalid
b2 is invalid
t is invalid

Resources