taptest
function taptest( actual, expect [, compare [, message ]] ) --> msg function taptest( diagnostic ) --> msg function taptest() --> msg
Description
This function behaves differently based on the number of arguments.
-
It can check actual values versus expected ones.
-
It can print diagnostic.
-
Or it can print tests summary when called without arguments.
All the output is done in the Test Anything Protocol (TAP) format. In case of failure some information are appended, like source position, actual value, etc.
For a more detailed explanation of its behaviour, refer to the next section.
Parameters
- actual
-
The actual value got from the code under test.
- expect
-
The expected value
- compare
-
The compare function. If it is given as 3-rd or 4-th argument, this function will be called with actual, expected as argument. If it return true the test will be assumed to success, otherwise it will be assumed to be failed. If no compare function is given, the == operator will be used as default.
- message
-
If a message string is given as 3-rd or 4-th argument, it will be appended to the TAP formatted line, only in case of failing test. This is ment as a way to give additional information about the failure.
- diagnostic
-
When called with just one string argument, a TAP diagnostic block will be printed. A # will be prepended to each line of the diagnostic message.
Return Values
- msg
-
Returns a string containing the same message written to the stdout. This message is a TAP check line or a sequence of TAP diagnostic lines.
Code
local test_count = 0 local fail_count = 0 --ZFUNC-taptest-v0 local function taptest( ... ) --> msg local function diagnostic( desc ) local msg = "#\n#########\n# "..desc:gsub( "\n", "\n# " ) print( msg ) return msg end local function print_summary() local msg = "" if fail_count == 0 then msg = msg..diagnostic( "all is right" ) else msg = msg..diagnostic( fail_count.." tests failed" ) end local plan = "1.."..test_count print( plan ) return msg.."\n"..plan end local function do_check(got, expected, a, b) -- Extra arg parse and defaults local checker, err if "string" == type(a) then err = a end if "string" == type(b) then err = b end if not err then err = "" end if "function" == type(a) then checker = a end if "function" == type(b) then checker = b end if not checker then checker = function( e, g ) return e == g end end -- Check the condition test_count = test_count + 1 local ok, info = checker( got, expected ) -- Generate TAP line local msg = "" if ok then msg = msg.."ok "..test_count else fail_count = fail_count + 1 -- Find position in source local stackup = 2 local i = debug.getinfo(stackup) while i.source == "=(tail call)" do stackup = stackup + 1 i = debug.getinfo(stackup) end msg = msg .."not ok " .. test_count .. " - " ..i.source:match( "([^@/\\]*)$" )..":"..i.currentline..". " .."Expectation ["..tostring( expected ).."] " .."does not match with ["..tostring( got ).."]. " ..err end if info then msg = msg.." "..info end print(msg) return msg end local narg = select( "#", ... ) if 0 == narg then return print_summary() elseif 1 == narg then return diagnostic( select( 1, ... ) ) elseif 4 >= narg then return do_check( ... ) end return nil, "Too many arguments" end return taptest
Examples
local t = require "taptest" local taptest = require "taptest" -- taptest is both the "Unit under test" (taptest) and the "Test framework" (t) -- To avoid confusion (as much as it is possible) tf will be always used in its -- easest form: it just checks that the two argument are equals. -- Since taptest always returns what it print on stdout, the returned -- value of taptest is checked -- wrap the taptest to avoid printing test results on the stdout local fake_test_count = 1 local taptest_wrapped = taptest local function taptest( ... ) if 1 < select( "#", ... ) then print( "ok "..fake_test_count ) end fake_test_count = fake_test_count + 2 local _p = print print = function() end local result = taptest_wrapped( ... ) print = _p return result end t( taptest( 1, 1 ), "ok 1" ) -- Note: since at each line two test will be done (one for taptest and one for tf) -- the test counter step is 2, not 1 t( taptest( 1, 1 ), "ok 3" ) -- Additional infos when the test fails t( taptest( 1, 2 ), "not ok 5 - taptest.ex1.lua:18. Expectation [2] does not match with [1]. " ) -- Custom infos on fail t( taptest( 1, 2, "Not good!" ), "not ok 7 - taptest.ex1.lua:18. Expectation [2] does not match with [1]. Not good!" ) -- Custom compare function t( taptest( 1, 2, function( a, b ) return a < b end ), "ok 9" ) t( taptest( 2, 1, function( a, b ) return a < b end ), "not ok 11 - taptest.ex1.lua:18. Expectation [1] does not match with [2]. " ) -- Custom compare function and message t( taptest( 2, 1, function( a, b ) return a < b end, "Not good!" ), "not ok 13 - taptest.ex1.lua:18. Expectation [1] does not match with [2]. Not good!" ) t( taptest( 2, 1, "Not good!", function( a, b ) return a < b end ), "not ok 15 - taptest.ex1.lua:18. Expectation [1] does not match with [2]. Not good!" ) -- Single argument = Tap diagnostic t( taptest( "new\nsuite" ), "#\n#########\n# new\n# suite" ) -- Checker function can add useful information t( taptest( 1, 1, function( a, b ) return a == b, "- additional info" end ), "ok 18 - additional info" ) t( taptest( 1, 2, function( a, b ) return a == b, "- additional info" end ), "not ok 20 - taptest.ex1.lua:18. Expectation [2] does not match with [1]. - additional info" ) -- No argument = Summary and final plan t( taptest(), "#\n#########\n# 6 tests failed\n1..21" ) t() -- In case all the tests are successful, the line -- # all is right -- will be substitued to the '# 5 tests failed' one