Module:UnitTests
Ce module est destiné à faciliter la mise en place de test unitaire d'autres modules ou de modèles. Il faut créer un module qui génère les tests, et une page qui affiche les résultats.
Voici un exemple simple : Module:Banane/Test, test unitaire du module:Banane :
-- Test unitaire pour [[Module:Banane]]. Cliquer sur « Discussion » pour voir le résultat du test.
local p = require('Module:UnitTests')
function p:test_hello()
self:preprocess_equals('{{#invoke:Banane | hello}}', 'Hello, world!')
end
return p
Le résultat est affiché sur Discussion module:Banane/Test par le code {{#invoke: Banane/Test | run_tests}}
.
Les noms des fonctions de tests comme test_hello()
ci-dessus doivent commencer par « test ». Elles sont affichées par ordre alphabétique.
Utilisation
[modifier le code]Fonctions exportables :
run_tests( frame )
– exécute toutes les fonctions commençant par « test » et affiche les résultats en tableau. Le paramètre differs_at=1 ajoute une colonne avec la position du premier caractère qui diverge du résultat prévu.
Methods (à traduire)
[modifier le code]run_tests
[modifier le code]run_tests(differs_at)
: Runs all tests.
If "differs_at=1" is specified, a column will be added showing the first character position where the expected and actual results differ. Normally used on talk page of unit tests.
{{#invoke:Bananas/testcases|run_tests}}
preprocess_equals
[modifier le code]preprocess_equals(text, expected)
: Gives a piece of wikitext to preprocess and an expected resulting value. Scripts and templates can be invoked in the same manner they would be in a page.
self:preprocess_equals('{{#invoke:Bananas | hello}}', 'Hello, world!')
preprocess_equals_many
[modifier le code]preprocess_equals_many(prefix, suffix, cases)
: Performs a series of preprocess_equals() calls on a set of given pairs. Automatically adds the given prefix and suffix to each text.
self:preprocess_equals_many('{{#invoke:BananasArgs | add |', '}}', {
{'2|3', '5'},
{'-2|2', '0'},
})
preprocess_equals_preprocess
[modifier le code]preprocess_equals_preprocess(text, expected)
: Gives two pieces of wikitext to preprocess and determines if they produce the same value. Useful for comparing scripts to existing templates.
self:preprocess_equals_preprocess('{{#invoke:Bananas | hello}}', '{{Hello}}')
preprocess_equals_preprocess_many
[modifier le code]preprocess_equals_preprocess_many(prefix, suffix, cases)
: Performs a series of preprocess_equals_preprocess() calls on a set of given pairs. The prefix/suffix supplied for both arguments is added automatically. If in any case the second part is not specified, the first part will be used.
self:preprocess_equals_preprocess_many('{{#invoke:Foo | spellnum |', '}}', '{{spellnum', '}}', {
{'2'}, -- equivalent to {'2','2'},
{'-2', '-2.0'},
})
equals
[modifier le code]equals(name, actual, expected)
: Gives a computed value and the expected value, and checks if they are equal according to the == operator. Useful for testing modules that are designed to be used by other modules rather than using #invoke.
self:equals('Simple addition', 2 + 2, 4)
equals_deep
[modifier le code]equals_deep(name, actual, expected)
: Like equals, but handles tables by doing a deep comparison. Neither value should contain circular references, as they are not handled by the current implementation and may result in an infinite loop.
self:equals_deep('Table comparison', createRange(1,3), {1,2,3})
Voir aussi
[modifier le code]La documentation de ce module est générée par le modèle {{Documentation module}}.
Elle est incluse depuis sa sous-page de documentation. Veuillez placer les catégories sur cette page-là.
Les éditeurs peuvent travailler dans le bac à sable (créer).
Voir les statistiques d'appel depuis le wikicode sur l'outil wstat et les appels depuis d'autres modules.
-- UnitTester provides unit testing for other Lua scripts. For details see [[Wikipedia:Lua#Unit_testing]].
-- For user documentation see talk page.
local UnitTester = {}
local frame, tick, cross
local header_text = 'Text'
local header_expected = 'Expected'
local header_actual = 'Actual'
local header_differs_at = 'Differs at'
local result_table_header = [=[
{|class="wikitable"
|+ style="text-align:left; margin-bottom:1em;"| %s :
!scope=col|
!scope=col| %s
!scope=col| %s
!scope=col| %s]=]
result_table_header = result_table_header:format( '%s', header_text, header_expected, header_actual )
local result_table_header_differs_at = '!scope=col| ' .. header_differs_at
local result_table = ''
local num_failures = 0
function first_difference(s1, s2)
if s1 == s2 then return '' end
local max = math.min(#s1, #s2)
for i = 1, max do
if s1:sub(i,i) ~= s2:sub(i,i) then return i end
end
return max + 1
end
function UnitTester:preprocess_equals(text, expected, options)
if options and options.before then
result_table = result_table .. options.before
end
local actual = frame:preprocess(text)
if actual == expected then
result_table = result_table .. '| ' .. tick
else
result_table = result_table .. '| ' .. cross
num_failures = num_failures + 1
end
local maybe_nowiki = (options and options.nowiki) and mw.text.nowiki or function(...) return ... end
local differs_at = self.differs_at and (' \n| ' .. first_difference(expected, actual)) or ''
result_table = result_table
.. '\n| ' .. mw.text.nowiki(text)
.. '\n| ' .. maybe_nowiki(expected)
.. '\n| ' .. maybe_nowiki(actual) .. differs_at
.. '\n|-\n'
end
function UnitTester:preprocess_equals_many(prefix, suffix, cases, options)
if options and options.before then
result_table = result_table .. options.before
options.before = nil
end
if options and options.beforeEach then
options.before = options.beforeEach
options.beforeEach = nil
end
for _, case in ipairs(cases) do
self:preprocess_equals(prefix .. case[1] .. suffix, case[2], options)
end
end
function UnitTester:preprocess_equals_preprocess(text1, text2, options)
if options and options.before then
result_table = result_table .. options.before
end
local actual = frame:preprocess(text1)
local expected = frame:preprocess(text2)
if actual == expected then
result_table = result_table .. '| ' .. tick
else
result_table = result_table .. '| ' .. cross
num_failures = num_failures + 1
end
local maybe_nowiki = (options and options.nowiki) and mw.text.nowiki or function(...) return ... end
local differs_at = self.differs_at and ('\n| ' .. first_difference(expected, actual)) or ''
result_table = result_table
.. '\n| ' .. mw.text.nowiki(text1)
.. '\n| ' .. maybe_nowiki(expected)
.. '\n| ' .. maybe_nowiki(actual) .. differs_at
.. '\n|-\n'
end
function UnitTester:preprocess_equals_preprocess_many(prefix1, suffix1, prefix2, suffix2, cases, options)
if options and options.before then
result_table = result_table .. options.before
options.before = nil
end
if options and options.beforeEach then
options.before = options.beforeEach
options.beforeEach = nil
end
for _, case in ipairs(cases) do
self:preprocess_equals_preprocess(prefix1 .. case[1] .. suffix1, prefix2 .. (case[2] and case[2] or case[1]) .. suffix2, options)
end
end
function UnitTester:equals(name, actual, expected, options)
if options and options.before then
result_table = result_table .. options.before
end
if actual == expected then
result_table = result_table .. '| ' .. tick
else
result_table = result_table .. '| ' .. cross
num_failures = num_failures + 1
end
local maybe_nowiki = (options and options.nowiki) and mw.text.nowiki or function(...) return ... end
local differs_at = self.differs_at and (' \n| ' .. first_difference(expected, actual)) or ''
result_table = result_table
.. '\n| ' .. name
.. '\n| ' .. maybe_nowiki(tostring(expected))
.. '\n| ' .. maybe_nowiki(tostring(actual)) .. differs_at
.. '\n|-\n'
end
local function deep_compare(t1, t2, ignore_mt)
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then return false end
if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
local mt = getmetatable(t1)
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
for k1, v1 in pairs(t1) do
local v2 = t2[k1]
if v2 == nil or not deep_compare(v1, v2) then return false end
end
for k2, v2 in pairs(t2) do
local v1 = t1[k2]
if v1 == nil or not deep_compare(v1, v2) then return false end
end
return true
end
function val_to_str(v)
if type(v) == 'string' then
v = mw.ustring.gsub(v, '\n', '\\n')
if mw.ustring.match(mw.ustring.gsub(v, '[^\'"]', ''), '^"+$') then
return "'" .. v .. "'"
end
return '"' .. mw.ustring.gsub(v, '"', '\\"' ) .. '"'
else
return type(v) == 'table' and table_to_str(v) or tostring(v)
end
end
function table_key_to_str(k)
if type(k) == 'string' and mw.ustring.match(k, '^[_%a][_%a%d]*$') then
return k
else
return '[' .. val_to_str(k) .. ']'
end
end
function table_to_str(tbl)
local result, done = {}, {}
for k, v in ipairs(tbl) do
table.insert(result, val_to_str(v))
done[k] = true
end
for k, v in pairs(tbl) do
if not done[k] then
table.insert(result, table_key_to_str(k) .. '=' .. val_to_str(v))
end
end
return '{' .. table.concat(result, ',') .. '}'
end
function UnitTester:equals_deep(name, actual, expected, options)
if options and options.before then
result_table = result_table .. options.before
end
if deep_compare(actual, expected) then
result_table = result_table .. '| ' .. tick
else
result_table = result_table .. '| ' .. cross
num_failures = num_failures + 1
end
local maybe_nowiki = (options and options.nowiki) and mw.text.nowiki or function(...) return ... end
local actual_str = val_to_str(actual)
local expected_str = val_to_str(expected)
local differs_at = self.differs_at and (' \n| ' .. first_difference(expected_str, actual_str)) or ''
result_table = result_table
.. ' \n| ' .. name
.. ' \n| ' .. maybe_nowiki(expected_str)
.. ' \n| ' .. maybe_nowiki(actual_str) .. differs_at
.. '\n|-\n'
end
function UnitTester:run(frame_arg)
frame = frame_arg
self.frame = frame
self.differs_at = frame.args['differs_at']
tick = frame:preprocess('{{fait}}')
cross = frame:preprocess('{{Croix3}}')
local table_header = result_table_header
if self.differs_at then
table_header = table_header .. result_table_header_differs_at
end
-- Sort results into alphabetical order.
local self_sorted = {}
for key,value in pairs(self) do
if key:find('^test') then
table.insert(self_sorted, key)
end
end
table.sort(self_sorted)
-- Add results to the results table.
for i,value in ipairs(self_sorted) do
result_table = result_table
.. table_header:format( value )
.. '\n|-\n'
self[value](self)
result_table = result_table .. '|}' .. (i < #self_sorted and '\n\n' or '')
end
local printResult = [=[<span style="color:#008000">'''All tests passed.'''</span>]=]
if num_failures > 0 then
printResult = [=[<span style="color:#800000">''']=] .. num_failures .. [=[ tests failed.'''[[Catégorie:Module dont les tests échouent]]</span>]=]
end
return printResult .. '\n\n' .. frame:preprocess(result_table)
end
function UnitTester:new()
local o = {}
setmetatable(o, self)
self.__index = self
return o
end
local p = UnitTester:new()
function p.run_tests(frame) return p:run(frame) end
return p