Search code examples
rubytestingargument-error

Is there a way to test Argument Errors from within a method to return true or false?


I am trying to get used to testing my code with simple driver snippets and want to test whether an Argument Error is thrown without escaping out of the program. Here is the code I am using

class Die
  def initialize(sides)
    @sides=sides
    unless @sides>0
      raise ArgumentError.new("Your number sucks, yo")
    end
  end

  #returns the number of sides of a die
  def sides
    @sides
  end

  #generates a random die roll based on the number of sides
  def roll
    rand(@sides)+1
  end
end

And here is what I am attempting to call to get a test.

p bad=Die.new(0)=="Your number sucks, yo"

What I want it to return is "true". What it does return in the terminal is :

w3p1_refact.rb:33:in `initialize': Your number sucks, yo (ArgumentError)
    from w3p1_refact.rb:69:in `new'
    from w3p1_refact.rb:69:in `<main>'

Can I rewrite this to return what I am looking for?


Solution

  • From the documentation of Exception

    When an exception has been raised but not yet handled (in rescue, ensure, at_exit and END blocks) the global variable $! will contain the current exception and $@ contains the current exception’s backtrace.

    So once I have just raised exception in the $! global variable, I can use Exception#message method, which returns the exception’s message or name.

    You use Kernel#raise

    With no arguments, raises the exception in $! or raises a RuntimeError if $! is nil. With a single String argument, raises a RuntimeError with the string as a message. Otherwise, the first parameter should be the name of an Exception class (or an object that returns an Exception object when sent an exception message). The optional second parameter sets the message associated with the exception, and the third parameter is an array of callback information. Exceptions are caught by the rescue clause of begin...end blocks.

    I would do as below :

    class Die
      def initialize(sides)
        @sides=sides
        unless @sides>0
          raise ArgumentError.new("Your number sucks, yo")
          # As per the doc you could write the above line as below also
          # raise ArgumentError, "Your number sucks, yo"
        end
      end
    
      #returns the number of sides of a die
      def sides
        @sides
      end
    
      #generates a random die roll based on the number of sides
      def roll
        rand(@sides)+1
      end
    end
    
    Die.new(0) rescue $!.message == "Your number sucks, yo"
    # => true
    

    The above inline rescue code can be written as also :

    begin
      Die.new(0)
    rescue ArgumentError => e
      bad = e.message
    end 
    bad == "Your number sucks, yo" # => true