1.4.1.18. fejezet, Hibakeresés és tesztelés

A hibakeresés az irb interaktív alkalmazással, vagy a ruby-val végezhető. Ruby-nak egy speciális modulját beemelve és azzal futtatva a kódot, szintén hibakeresést érhetünk el. Ezt a modult azonban paraméterül kapja a Ruby, nincs rá állandóan szükségünk.

ruby –r debug debug_test.rb

az irb-t alkalmazva a következő opcionális paraméterek használhatóak:

-f Suppress read of ~/.irbrc
-m Bc mode (load mathn, fraction or matrix are availa-ble)
-d Set $DEBUG to true (same as 'ruby -d')
-r load-module Same as `ruby -r'
-I path Specify $LOAD_PATH directory
--inspect Use `inspect' for output (default except for bc mode)
--noinspect Don't use inspect for output
--readline Use Readline extension module
--noreadline Don't use Readline extension module
--prompt prompt-mode
--prompt-mode prompt-mode Switch prompt mode. Pre-defined prompt modes are `default', `simple', `xmp' and `inf-ruby'
--inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs. Suppresses --readline.
--simple-prompt Simple prompt mode
--noprompt No prompt mode
--tracer Display trace for each execution of commands.
--back-trace-limit n Display backtrace top n and tail n. The default value is 16.
--irb_debug n Set internal debug level to n (not for popular use)
-v, --version Print the version of irb

Ha a Ruby-t használjuk, a következő parancsokat adhatjuk a hibakeresőnek (dbg):

b[reak] [file|class:]<line|method> b[reak] [class.]<line|method> set breakpoint to some position
wat[ch] <expression> set watchpoint to some expression
cat[ch] <an Exception> set catchpoint to an exception
b[reak] list breakpoints
cat[ch] show catchpoint
del[ete][ nnn] delete some or all breakpoints
disp[lay] <expression> add expression into display expression list
undisp[lay][ nnn] delete one particular or all display expressions
c[ont] run until end or breakpoint
s[tep][ nnn] step (into code) 1 line or to line:nnn
n[ext][ nnn] go over one line or till line nnn
w[here] display frames
f[rame] alias for where
l[ist][ (-|nn-mm)] list program, - lists backwards nn-mm lists given lines
up[ nn] move to higher frame
down[ nn] move to lower frame
fin[ish] return to outer frame
tr[ace] (on|off) Set trace mode of current thread
tr[ace] (on|off) all Set trace mode of all threads
q[uit] exit from debugger
v[ar] g[lobal] show global variables
v[ar] l[ocal] show local variables
v[ar] i[nstance] <object> show instance variables of object
v[ar] c[onst] <object> show constants of object
m[ethod] i[nstance] <obj> show methods of object
m[ethod] <class|module> show instance methods of class or module
th[read] l[ist] list all threads
th[read] c[ur[rent]] show current thread
th[read] [sw[itch]] <nnn> switch thread context to nnn
th[read] stop <nnn> stop thread nnn
th[read] resume <nnn> resume thread nnn
p expression evaluate expression and print its value
h[elp] print this help
<everything else> Evaluate

Unit teszt

Az alapötlet abból áll, hogy kijelentéseket (assert) tehetünk, ami eredménye lehet az előzőleg kiadott utasításoknak. Az eredmény teljesülését ellenőrizzük az assert_equal, vagy egyszerűen assert metódussal. Először is be kell emelnünk a kódba a test/unit modult a require parancsal. Aztán a teszt osztályt a TestCase osztály leszármazottjává kell tenni:

class MyTest < Test::Unit::TestCase

Aztán metódusokat kell írni ebbe az osztályba, amik a test szóval kezdődnek (pl.: testMyClass), és állításokat tartalmaznak.

require 'test/unit'
 
class TestClass	
  def initialize( aVal )
    @val = aVal * 10
  end
 
  def getVal
    return @val
  end
end
 
class MyTest < Test::Unit::TestCase		
  def test1
    t = TestClass.new(10)
    assert_equal(100, t.getVal)	
    assert_equal(101, t.getVal)	
    assert(100 != t.getVal)									
  end
 
  def test2		
    assert_equal(1000,TestClass.new(100).getVal)			
  end
end

Eredmény:

Loaded suite /home/pzoli/workspace/Ruby examples/ch18/test1
Started
F.
Finished in 0.11939 seconds.
 
  1) Failure:
test1(MyTest) [/home/pzoli/workspace/Ruby examples/ch18/test1.rb:19]:
<101> expected but was
<100>.
 
2 tests, 3 assertions, 1 failures, 0 errors

Az assert_kind_of metódus az objektum osztályát ellenőrzi:

  1. require 'test/unit'
  2. require_relative("buggy") # TODO 1.9!=1.8
  3.  
  4. class MyTest < Test::Unit::TestCase
  5.  
  6. def setup
  7. @game = TestMod::Adventure.new
  8. end
  9.  
  10. def test1
  11. @game.treasures.each{ |t|
  12. assert(t.value < 2000, "FAIL: #{t} t.value = #{t.value}" )
  13. }
  14. end
  15.  
  16. def test2
  17. assert_kind_of( TestMod::Adventure::Map, @game.map)
  18. assert_kind_of( Array, @game.map)
  19. end
  20.  
  21. def teardown
  22. @game.endgame
  23. end
  24. end

Eredménye:

Loaded suite /home/pzoli/workspace/Ruby examples/ch18/test2
Started
 
At end of game, map is: 
#<TestMod::Adventure::Map:0x9e8de34 @rooms=[#<TestMod::Adventure::Room:0x9e8df24 @name="\nCrystal Grotto", @treasures=[]>, #<TestMod::Adventure::Room:0x9e8ded4 @name="\nDark Cave", @treasures=[#<TestMod::Adventure::Treasure:0x9e8dfb0 @name="A sword", @value=800>]>, #<TestMod::Adventure::Room:0x9e8de84 @name="\nForest Glade", @treasures=[#<TestMod::Adventure::Treasure:0x9e8df88 @name="A dragon Horde", @value=550>, #<TestMod::Adventure::Treasure:0x9e8df60 @name="An Elvish Ring", @value=3000>]>]>
Destroying map....
Map is now: 
[]
F
At end of game, map is: 
#<TestMod::Adventure::Map:0x9e8d524 @rooms=[#<TestMod::Adventure::Room:0x9e8d628 @name="\nCrystal Grotto", @treasures=[]>, #<TestMod::Adventure::Room:0x9e8d5c4 @name="\nDark Cave", @treasures=[#<TestMod::Adventure::Treasure:0x9e8d6b4 @name="A sword", @value=800>]>, #<TestMod::Adventure::Room:0x9e8d574 @name="\nForest Glade", @treasures=[#<TestMod::Adventure::Treasure:0x9e8d68c @name="A dragon Horde", @value=550>, #<TestMod::Adventure::Treasure:0x9e8d664 @name="An Elvish Ring", @value=3000>]>]>
Destroying map....
Map is now: 
[]
F
!>Finished in 0.008306 seconds.
 
  1) Failure:
test1(MyTest) [/home/pzoli/workspace/Ruby examples/ch18/test2.rb:12]:
FAIL: #<TestMod::Adventure::Treasure:0x9e8df60> t.value = 3000
 
  2) Failure:
test2(MyTest) [/home/pzoli/workspace/Ruby examples/ch18/test2.rb:18]:
Expected #<TestMod::Adventure::Map:0x9e8d524
 @rooms=
  [#<TestMod::Adventure::Room:0x9e8d628
    @name="\nCrystal Grotto",
    @treasures=[]>,
   #<TestMod::Adventure::Room:0x9e8d5c4
    @name="\nDark Cave",
    @treasures=
     [#<TestMod::Adventure::Treasure:0x9e8d6b4 @name="A sword", @value=800>]>,
   #<TestMod::Adventure::Room:0x9e8d574
    @name="\nForest Glade",
    @treasures=
     [#<TestMod::Adventure::Treasure:0x9e8d68c
       @name="A dragon Horde",
       @value=550>,
      #<TestMod::Adventure::Treasure:0x9e8d664
       @name="An Elvish Ring",
       @value=3000>]>]> to be a kind of Array, not TestMod::Adventure::Map.
 
2 tests, 5 assertions, 2 failures, 0 errors, 0 skips
 
Test run options: --seed 47635

A buggy.rb fájl a könyv mintaprogramjainak 18. fejezeténél, a ch18 könyvtárában található. Elég terjedelmes, ezért itt most nem szerepel. A lényeg a teszt unit-ban van, amiben található még két metódus, a setup és a teardown. A setup a tesztesetek (test1,test2) előtt, a teardown az utána fut le. Fenti esetben setup, test1, teardown, setup, test2, teardown sorrendben.

Kijelentések Unit teszteléshez

assert(boolean, message=nil)
Asserts that boolean is not false or nil.

assert_block(message="assert_block failed.") {|| ...}
The assertion upon which all other assertions are based. Passes if the block yields true.

assert_equal(expected, actual, message=nil)
Passes if expected == +actual.

assert_in_delta(expected_float, actual_float, delta, message="")
Passes if expected_float and actual_float are equal within delta tolerance.

assert_instance_of(klass, object, message="")
Passes if object .instance_of? klass assert_kind_of(klass, object, message="") Passes if object .kind_of? klass

assert_match(pattern, string, message="")
Passes if string =~ pattern.

assert_nil(object, message="")
Passes if object is nil.

assert_no_match(regexp, string, message="")
Passes if regexp !~ string

assert_not_equal(expected, actual, message="")
Passes if expected != actual

assert_not_nil(object, message="")
Passes if ! object .nil?

assert_not_same(expected, actual, message="")
Passes if ! actual .equal? expected

assert_nothing_raised(*args) {|| ...}
Passes if block does not raise an exception.

assert_nothing_thrown(message="", &proc)
Passes if block does not throw anything.

assert_operator(object1, operator, object2, message="")
Compares the +object1+ with +object2+ using operator. Passes if object1.send(operator, object2) is true.

assert_raise(*args) {|| ...}
Passes if the block raises one of the given exceptions.

assert_raises(*args, &block)
Alias of assert_raise. (Deprecated in Ruby 1.9, and to be removed in 2.0).

assert_respond_to(object, method, message="")
Passes if object .respond_to? method

assert_same(expected, actual, message="")
Passes if actual .equal? expected (i.e. they are the same instance).

assert_send(send_array, message="")
Passes if the method send returns a true value.

assert_throws(expected_symbol, message="", &proc)
Passes if the block throws expected_symbol

build_message(head, template=nil, *arguments)
Builds a failure message. head is added before the template and argu-ments replaces the ’?’s positionally in the template.

flunk(message="Flunked")
flunk always fails.