Module:UnitTests
Appearance
From mediawiki.org
UnitTests provides a unit test facility that can be used by other scripts using require. Following is an example:
-- Unit tests for [[Module:Bananas]]. Click talk page to run tests. localp=require('Module:UnitTests') functionp:test_hello() self:preprocess_equals('{{#invoke:Bananas| hello}}','Hello World!') end returnp
This would be executed with {{#invoke: Bananas/testcases | run_tests}}. Test methods like test_hello above must begin with "test".
Methods
[edit ]run_tests
[edit ]run_tests- Runs all tests. Normally used on talk page of unit tests.
{{#invoke:Bananas/testcases|run_tests}}
- If
differs_at=1is specified, a column will be added showing the first character position where the expected and actual results differ.
{{#invoke:Bananas/testcases|run_tests|differs_at=1}}
preprocess_equals
[edit ]preprocess_equals(text, expected, options)- 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!',{nowiki=1})
preprocess_equals_many
[edit ]preprocess_equals_many(prefix, suffix, cases, options)- 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'}, },{nowiki=1})
preprocess_equals_preprocess
[edit ]preprocess_equals_preprocess(text, expected, options)- 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}}',{nowiki=1})
preprocess_equals_preprocess_many
[edit ]preprocess_equals_preprocess_many(prefix, suffix, cases, options)- 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'}, },{nowiki=1})
equals
[edit ]equals(name, actual, expected, options)- 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,{nowiki=1})
equals_deep
[edit ]equals_deep(name, actual, expected, options)- 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},{nowiki=1})
See also
[edit ]- Module:ScribuntoUnit – alternative unit test module
The above documentation is transcluded from Module:UnitTests/doc. (edit | history)
Editors can experiment in this module’s sandbox (create | mirror) and testcases (create) pages.
Subpages of this module.
Editors can experiment in this module’s sandbox (create | mirror) and testcases (create) pages.
Subpages of this module.
-- UnitTester provides unit testing for other Lua scripts. For details see [[Wikipedia:Lua#Unit_testing]]. -- For user documentation see talk page. localUnitTester={} localframe,tick,cross localresult_table_header="{|class=\"wikitable\"\n! !! Text !! Expected !! Actual" localresult_table='' localnum_tests=0; localnum_failures=0 functionfirst_difference(s1,s2) ifs1==s2thenreturn''end localmax=math.min(#s1,#s2) fori=1,maxdo ifs1:sub(i,i)~=s2:sub(i,i)thenreturniend end returnmax+1 end functionUnitTester:preprocess_equals(text,expected,options) num_tests=num_tests+1; localactual=frame:preprocess(text) ifactual==expectedthen result_table=result_table..'| '..tick else result_table=result_table..'| '..cross num_failures=num_failures+1 end localmaybe_nowiki=(optionsandoptions.nowiki)andmw.text.nowikiorfunction(...)return...end localdiffers_at=self.differs_atand(' \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 functionUnitTester:preprocess_expect_error(text,options) num_tests=num_tests+1; localactual=frame:preprocess(text) localmaybe_nowiki=(optionsandoptions.nowiki)andmw.text.nowikiorfunction(...)return...end localcaught=frame:preprocess("{{#iferror:"..actual.."|ok|fail}}") ifcaught=="ok"then actual="Caught expected error: "..maybe_nowiki(actual) result_table=result_table..'| '..tick else result_table=result_table..'| '..cross num_failures=num_failures+1 end localdiffers_at=self.differs_atand' \n| 'or'' result_table=result_table..' \n| '..mw.text.nowiki(text)..' \n| '.."An error"..' \n| '..actual..differs_at.."\n|-\n" end functionUnitTester:preprocess_equals_many(prefix,suffix,cases,options) for_,caseinipairs(cases)do localtext=prefix..case[1]..suffix if(notcase.error)then self:preprocess_equals(text,case[2],options) else self:preprocess_expect_error(text,options) end end end functionUnitTester:preprocess_equals_preprocess(text1,text2,options) num_tests=num_tests+1; localactual=frame:preprocess(text1) localexpected=frame:preprocess(text2) ifactual==expectedthen result_table=result_table..'| '..tick else result_table=result_table..'| '..cross num_failures=num_failures+1 end localmaybe_nowiki=(optionsandoptions.nowiki)andmw.text.nowikiorfunction(...)return...end localdiffers_at=self.differs_atand(' \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 functionUnitTester:preprocess_equals_preprocess_many(prefix1,suffix1,prefix2,suffix2,cases,options) for_,caseinipairs(cases)do localtext1=prefix1..case[1]..suffix1 if(notcase.error)then localtext2=prefix2..(case[2]andcase[2]orcase[1])..suffix2 self:preprocess_equals_preprocess(text1,text2,options) else self:preprocess_expect_error(text1,options) end end end functionUnitTester:equals(name,actual,expected,options) num_tests=num_tests+1; ifactual==expectedthen result_table=result_table..'| '..tick else result_table=result_table..'| '..cross num_failures=num_failures+1 end localmaybe_nowiki=(optionsandoptions.nowiki)andmw.text.nowikiorfunction(...)return...end localdiffers_at=self.differs_atand(' \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 localfunctiondeep_compare(t1,t2,ignore_mt) localty1=type(t1) localty2=type(t2) ifty1~=ty2thenreturnfalseend ifty1~='table'andty2~='table'thenreturnt1==t2end localmt=getmetatable(t1) ifnotignore_mtandmtandmt.__eqthenreturnt1==t2end fork1,v1inpairs(t1)do localv2=t2[k1] ifv2==nilornotdeep_compare(v1,v2)thenreturnfalseend end fork2,v2inpairs(t2)do localv1=t1[k2] ifv1==nilornotdeep_compare(v1,v2)thenreturnfalseend end returntrue end functionval_to_str(v) iftype(v)=='string'then v=mw.ustring.gsub(v,'\n','\\n') ifmw.ustring.match(mw.ustring.gsub(v,'[^\'"]',''),'^"+$')then return"'"..v.."'" end return'"'..mw.ustring.gsub(v,'"','\\"')..'"' else returntype(v)=='table'andtable_to_str(v)ortostring(v) end end functiontable_key_to_str(k) iftype(k)=='string'andmw.ustring.match(k,'^[_%a][_%a%d]*$')then returnk else return'['..val_to_str(k)..']' end end functiontable_to_str(tbl) localresult,done={},{} fork,vinipairs(tbl)do table.insert(result,val_to_str(v)) done[k]=true end fork,vinpairs(tbl)do ifnotdone[k]then table.insert(result,table_key_to_str(k)..'='..val_to_str(v)) end end return'{'..table.concat(result,',')..'}' end functionUnitTester:equals_deep(name,actual,expected,options) num_tests=num_tests+1; ifdeep_compare(actual,expected)then result_table=result_table..'| '..tick else result_table=result_table..'| '..cross num_failures=num_failures+1 end localmaybe_nowiki=(optionsandoptions.nowiki)andmw.text.nowikiorfunction(...)return...end localactual_str=val_to_str(actual) localexpected_str=val_to_str(expected) localdiffers_at=self.differs_atand(' \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 functionUnitTester:run(frame_arg) frame=frame_arg self.frame=frame self.differs_at=frame.args['differs_at'] localdisplayMode=frame.args.displayModeor'table'; tick=frame:preprocess('{{Tick}}') cross=frame:preprocess('{{Cross}}') localtable_header=result_table_header ifself.differs_atthen table_header=table_header..' !! Differs at' end -- Sort results into alphabetical order. localself_sorted={} forkey,valueinpairs(self)do ifkey:find('^test')then table.insert(self_sorted,key) end end table.sort(self_sorted) -- Add results to the results table. fori,valueinipairs(self_sorted)do result_table=result_table.."\n\n'''"..value.."''':\n"..table_header.."\n|-\n" self[value](self) result_table=result_table.."|}" end if(displayMode=='short')then localresult_text=string.format( "success: %d, error: %d", num_tests-num_failures, num_failures ); if(num_failures>0)then result_text='<span class="error">'..result_text.."</span>"; end returnresult_text; end return(num_failures==0and'<strong style="color:#008000;">All tests passed.</strong>'or'<strong class="error" style="font-size:100%;">'..num_failures..' tests failed.</strong>')..frame:preprocess(result_table) end functionUnitTester:new() localo={} setmetatable(o,self) self.__index=self returno end localp=UnitTester:new() functionp.run_tests(frame)returnp:run(frame)end returnp