= Picotest - simple reduced tests Picotest is a gem which allows to write complete test suites in a few lines, targeted primarily to test separated methods, algorithms or little snippets of code == Installation The install is as simple as execute the well-known gem install: sudo gem install picotest == Known Limitations & Issues * Reverse oracle test only can be compared using exact equals (see sqrt example) * Metaprogramming of input/output pairs are ugly * Oracle testing does not allow expectation test making difficult to debug failing tests * Complete lack of RDoc == Usage To use picotest you should write suites using suite method, each suite is defined by a hash and an optional description, the keys of the hash are used as input for the method and the corresponding values as expected output For example, to specify that the method receiving 1 should return 1, receiving 4 should return 2 and receiving 9 should return 3, use the following syntax: suite(1 => 1, 4 => 2, 9 => 3) Or: suite("integer sqrt", 1 => 1, 4 => 2, 9 => 3) After that, you should specify the test: suite("integer sqrt", 1 => 1, 4 => 2, 9 => 3).test(Math.method(:sqrt)) To run test, you should set the environment variable PICOTEST_RUN to 1 === Environment variables All environments are activated setting their value to 1, other values distinct from 1 will be ignored PICOTEST_RUN enables the execution of test PICOTEST_REPORT enables reports of test on standard output and disables Picotest::Fail exceptions when test fails PICOTEST_AUTOTEST enables tests of picotest itself PICOTEST_FAIL simulate failure of all tests, for picotest test purpose == Code Examples See samples directory under the gem root === Example 1: basic usage of suite and test method class X def foo(data) data.split(";")[2].to_f end end suite( "aaa;xxx;1.0;999" => 1.0, ";;3.2" => 3.2).test X.new.method(:foo) === Example 2: Input sets Both -2 and 2 should evaluate to 4 on lambda{|x|x*x} suite(_set(-2,2) => 4).test lambda{|x|x*x} === Example 3: Oracle testing Using a lambda as value on the hash can be used to specify a rule to validate output-input pairs, useful for oracle testing suite(4 => lambda{|y,x| y*y == x}).test Math.method(:sqrt) === Example 4: Oracle testing with input sets This time, the validation rule must considerate the imprecision of floats and check using a error margin suite(_set(1,2,3,4,5) => lambda{|y,x| (y*y - x).abs<0.00001}).test Math.method(:sqrt) suite(_set(*(1..100) ) => lambda{|y,x| (y*y - x).abs<0.00001}).test Math.method(:sqrt) === Example 5: Reversed oracle When valid inputs can be generated from output (i.e. float 1.0 output correspond to string "aaa;xxx;1.0;" input) class X def foo(data) data.split(";")[2].to_f end end suite( lambda{|x| ";;#{x}"} => _set(*(1..20).map(&:to_f)) ).test X.new.method(:foo) === Example 6: Testing methods of input objects suite( "a b" => ["a","b"]).test :split suite( lambda{|x|x.join " "} => _set(["a","b"], ["x","y","z","1"], ["aaa","bbb"]) ).test :split === Example 7: Testing multiple argument inputs class X def sum(*x) x.inject{|a,b| a+b} end end suite( 1 => 1, [1,2] => 3, [1,2,3] => 6 ).test X.new.method(:sum) suite(_set(*(1..100)) => lambda{|y,x| y == x}, _set([1,2],[3,4],[4,6],[5,6]) => lambda{|y,x1,x2| y == x1+x2}, _set([1,2,3],[3,4,5],[4,6,7],[5,6,7]) => lambda{|y,x1,x2,x3| y == x1+x2+x3} ).test X.new.method(:sum) === Example 8: Mocking instance variable class X def foo(data) data.split(";")[@fieldno].to_f end end suite(hash).test X.new.method(:foo).mock(:@fieldno => 2) === Example 9: Mocking method on receiver object class X attr_accessor :fieldno def foo(data) data.split(";")[fieldno].to_f end end suite(hash).test X.new.method(:foo).mock(:fieldno => 2) === Example 10: Mock objects class X attr_accessor :another_object def foo(data) data.split(";")[another_object.fieldno].to_f end end suite(hash).test X.new.method(:foo).mock(:another_object => mock(:fieldno => 2) ) === Example 11: Mocking method on receiver object class X attr_accessor :another_object def foo(data) another_object.invented_here_split(data,";")[@fieldno].to_f end end # you can specify the implementatioin of methods using lambdas suite(hash).test X.new.method(:foo).mock( :another_object => mock(:invented_here_split => lambda{|str,sep| str.split(sep) }), :@fieldno => 2 ) # Since :split.to_proc is equivalent to lambda{|str,sep| str.split(sep) }, you can also use: suite(hash).test X.new.method(:foo).mock( :another_object => mock(:invented_here_split => :split.to_proc), :@fieldno => 2 ) === Example 12: Exceptions class X def foo(a) raise ArgumentError unless a.kind_of? Numeric a*2 end end suite(1 => 2, 2 => 4, 3 => 6, "00" => _raise(ArgumentError)).test(X.new.method(:foo)) == Copying Copyright (c) 2012 Dario Seminara, released under the GPL License (see LICENSE)