Ruby Exception & Testing

Objetives

Exceptions

A special kind of object, an instance of the class Exception or a descendant of that class, indicates that something has gone wrong.

Exception flow

  • When something fails, an exception is raised -thrown-.

  • By default, Ruby programs terminate when an exception occurs.

  • A block of code can be executed as a handler -exception handler- if an exception raises during the execution of some other block of code.

  • Raising an exception means stopping the normal execution of the program and transferring the flow-of-control to the exception handling code -deal with the problem or exit the program completely-.

  • The consequent action depends on whether you have provided a rescue clause.

Exception methods

Method Description
Exception#class Returns the class of the receiver
Exception#backtrace Returns the backtrace of the error
Exception#message Returns the error message.
Kernel#raise Raise an error
class MaybeError < StandardError
  attr_reader :limit

  def initialize(message, limit)
    super(message)
    @limit = limit
  end
end

def maybe_error(number)
  limit = rand(1..10)
  message = "Error for number #{number} when limit is #{limit}"
  raise MaybeError.new(message, limit) if number < limit
end

number = 1
count_retry = 0
retry_limits = 3

begin
  count_retry += 1
  maybe_error(number)
rescue MaybeError => error
  puts "#{error.message} recued!"
  puts "Limit variable on maybe_error method => #{error.limit}"
  number += 1
  # count_retry <= retry_limits ? retry : raise
  if count_retry <= retry_limits
    retry
  else
    puts "Too many Retries!! retries number #{count_retry}"
  end  
ensure
  puts "This is always executed!"
end

puts "All is OK when number was #{number}" # show!

Exceptions Code

Unit testing

Ensures that all code meets quality standards before it's deployed. Over the course of the product development life cycle, saves time and money, and helps developers write better code, more efficiently.

Why Minitest

  • Simple

  • Easy to learn

  • Good TDD workflow

  • Randomized by default

  • Fast

Stages of a test

  • Setup
  • Execution
  • Assertion

require 'minitest/autorun'

class RelatedClassTest < Minitest::Test
  def test_ask_for_functionality
    # setup
    declare_variables
    declare_expectations
    # execution
    create_instances
    do_calculations
    call_methods
    # assertion
    assert
    assert_equal expected, result
    assert_nil result
    assert_empty object
    assert_includes collection, object
    assert_match pattern, object
    assert_raises(Error) { method }
  end
end

Implementation

Exercise

# frozen_string_literal: true
module Pokedex
  CATALOG = {
    bulbasaur: {
      type: ["grass", "poison"],
      weaknesses: ["fire", "psychic", "flying", "ice"]
    }
  }.freeze
end

# Pokemon test class to apply unit testing on it
class Pokemon
  include Pokedex

  attr_reader :species, :name, :level, :type, :weaknesses, :hp
  attr_accessor :current_move

  def initialize(species:, name:, level: 1)
    raise StandardError, 'species and name could not be empty' if species.empty? || name.empty?

    properties = Pokedex::CATALOG[species.to_sym]

    @species = species
    @name = name
    @type = properties[:type]
    @weaknesses = properties[:weaknesses]
    @level = level
    @max_health = 100
  end

  def prepare_for_battle
    @hp = @max_health
    @current_move = nil
  end

  def receive_damage(damage)
    @hp -= damage
  end

  def fainted?
    !hp.positive?
  end
end

# species = "bulbasaur"
# name = "Bulbi"
# level = 1

# pokemon = Pokemon.new(species: species, name: name, level: level)

# p pokemon

pokemon.rb

require 'minitest/autorun'
require_relative "pokemon"

class PokemonTest < Minitest::Test

  def test_create_a_new_pokemon
    # Setup
    species = "bulbasaur"
    name = "Bulbi"
    level = 1

    # Execution
    pokemon = Pokemon.new(species: species, name: name, level: level)

    # Assertion
    assert_instance_of(Pokemon, pokemon)
    # assert_equal("Bulbi", pokemon.name)
  end

  def test_pokemon_properties
    # Setup
    species = "bulbasaur"
    name = "Bulbi"
    level = 1
    type = Pokemon::CATALOG[:bulbasaur][:type]
    weaknesses = Pokemon::CATALOG[:bulbasaur][:weaknesses]

    # Execution
    pokemon = Pokemon.new(species: species, name: name, level: level)
    
    # Assertion
    assert_equal("Bulbi", pokemon.name)
    assert_equal("bulbasaur", pokemon.species)
    assert_equal(level, pokemon.level)
    assert_nil(pokemon.current_move)
    assert_nil(pokemon.hp)
  end

  def test_raise_error_when_name_is_empty
    # Setup
    species = "bulbasaur"
    name = ""
    level = 1

    # Assertion
    assert_raises(StandardError) do
      Pokemon.new(species: species, name: name, level: level)
    end
  end

  def test_raise_error_when_species_is_empty
    # Setup
    species = ""
    name = "Bulbi"
    level = 1

    # Assertion
    assert_raises(StandardError) do
      Pokemon.new(species: species, name: name, level: level)
    end
  end

  def test_prepare_for_battle
    # Setup
    species = "bulbasaur"
    name = "Bulbi"
    level = 1
    pokemon = Pokemon.new(species: species, name: name, level: level)

    # Execution
    pokemon.prepare_for_battle

    # Assertions
    assert_equal(100, pokemon.hp)
    assert_nil(pokemon.current_move)
  end

  def test_pokemon_receive_damage
    # 
  end
  
  def test_pokemon_fainted
    # 
  end

end

testing.rb

Made with Slides.com