Module:ScribuntoUnit/doc: Difference between revisions
en>Lucas Werkmeister (→Test module structure: recommend loading ScribuntoUnit *before* the module to be tested – otherwise, ScribuntoUnit’s Module:No globals import happens too late to detect any globals in the module under test, even though (I assume) the intention is that globals in the module being tested should also be forbidden; compare c:Special:Diff/641130578) |
m (1 revision imported) |
Latest revision as of 14:06, 11 June 2022
This module depends on the following other modules: |
This module provides unit tests for other Lua modules. To test a module, you must create a separate test module, usually located at Module:Module name/testcases
. The module is tested with the ScribuntoUnit module, which verifies that the operations defined in the test module produce the expected results.
Test module structure
To make a test module (test suite), start with the following code:
local ScribuntoUnit = require('Module:ScribuntoUnit')
local myModule = require('Module:MyModule') -- the module to be tested
local suite = ScribuntoUnit:new()
After you have done this you can add individual test functions to the suite
object. Any function that begins with test
is treated as a test. (Other functions will be ignored by ScribuntoUnit, but can be used in the tests themselves.)
function suite:testSomeCall()
self:assertEquals('expected value', myModule.someCall(123))
self:assertEquals('other expected value', myModule.someCall(456))
end
function suite:testSomeOtherCall()
self:assertEquals('expected value', myModule.someOtherCall(123))
self:assertEquals('other expected value', myModule.someOtherCall(456))
end
The tests you write should make assertions, and ScribuntoUnit will check whether those assertions are true. For example, assertEquals
checks that both of the arguments it is given are equal. If ScribuntoUnit doesn't find an assertion to be true, then the test will fail and an error message will be generated. The error message will show which assertion failed verification (other checks on the assertions are not made at this time).
To finish the test module, you need to return the suite
object.
return suite
Running the tests
The tests can be run in two ways: through the Lua debug console, and from a wiki page using #invoke. If you are running the tests through the debug console, use the code require('Module:MyModule/testcases').run()
. If you are running them from a wiki page, use the code {{#invoke:MyModule/testcases|run}}
. This will generate a table containing the results. It is also possible to display a more compact table by using the code {{#invoke:MyModule/testcases|run|displayMode=short}}
.
Tests
Error messages
The last parameter of all the test methods is a message that is displayed if validation fails.
self:assertEquals("expected value", myModule.someCall(123), "The call to myModule.someCall(123) didn't return the expected value.")
assertTrue, assertFalse
self:assertTrue(expression, message)
self:assertFalse(expression, message)
These test whether the given expression evaluates to true
or false
. Note that in Lua false
and nil
evaluate to false
, and everything else evaluates to true
.
self:assertTrue(2 + 2 == 4)
self:assertTrue('foo')
self:assertFalse(2 + 2 == 5)
self:assertFalse(nil)
assertStringContains
self:assertStringContains(pattern, s, plain, message)
This tests whether pattern
is found in the string s
. If plain
is true, then pattern
is interpreted as literal text; otherwise, pattern
is interpreted as a ustring pattern.
If the string is not found, the error message shows the values of pattern
and s
; if s
is more than 70 characters long then a truncated version is displayed. This method is useful for testing specific behaviours in complex wikitext.
self:assertStringContains("foo", "foobar") -- passes
self:assertStringContains("foo", "fobar") -- fails
self:assertStringContains(".oo", "foobar") -- passes: matches "foo"
self:assertStringContains(".oo", "foobar", true) -- fails: . is interpreted as a literal character
assertNotStringContains
self:assertNotStringContains(pattern, s, plain, message)
This is the opposite of assertStringContains
. The test will fail if pattern
is found in the string s
. If plain
is true, then pattern
is interpreted as literal text; otherwise, pattern
is interpreted as a ustring pattern.
self:assertNotStringContains("foo", "foobar") -- fails
self:assertNotStringContains("foo", "fobar") -- passes
self:assertNotStringContains(".oo", "foobar") -- fails: matches "foo"
self:assertNotStringContains(".oo", "foobar", true) -- passes: . is interpreted as a literal character
assertEquals
self:assertEquals(expected, actual, message)
This tests whether the first parameter is equal to the second parameter. If both parameters are numbers, the values are instead compared using assertWithinDelta
with delta 1e-8 (0.00000001) since numbers are represented as floating points with limited precision.
self:assertEquals(4, calculator.add(2, 2))
assertWithinDelta
self:assertWithinDelta(expected, actual, delta, message)
For two numbers, this tests whether the first is within a given distance (delta) from the second. This is useful to compare floating point numbers, which are used to represent numbers in the standard installation of Lua. (To be precise, it uses double-precision floating point numbers.) For example, on the version of Scribunto installed on the English Stiles.casa Wiki, the expression 0.3 – 0.2 == 0.1
evaluates to false
. This is because in practice, the expression 0.3 – 0.2
equals 0.09999999999999997780…
and the number 0.1
equals 0.10000000000000000555…
. The slight error between the two means that Lua does not consider them equal. Therefore, to test for equality between two floating point numbers, we should accept values within a small distance (delta) of each other, not just equal values. Note that this problem does not affect integers, which can be represented exactly using double-precision floating point numbers up to values of 2^53.
self:assertWithinDelta(0.1, calculator.subtract(0.3, 0.2), 1e-10)
assertDeepEquals
self:assertDeepEquals(expected, actual, message)
This tests whether the first parameter is equal to the second parameter. If the parameters are tables, they are compared recursively, and their __eq metamethods are respected.
self:assertDeepEquals(table1, table2)
assertParserFunctionEquals
self:assertParserFunctionEquals(expected, pfname, args, message)
This tests whether the first parameter equals a parser function call. The second parameter is the parser function name, and the third parameter is a table of the parser function arguments.
self:assertParserFunctionEquals(4, 'msg:add', {2, 2}) -- true if {{msg:add|2|2}} equals 4
This can be especially useful when used with the Scribunto {{#invoke:}}
parser function, e.g.:
-- useful to "#invoke" the base module when this is used in a "testcases" submodule
local basemodname = mw.title.new(mw.getCurrentFrame():getTitle()).baseText
self:assertParserFunctionEquals(expected, '#invoke', {basemodname, funcname, arg1, paramX = argParamX})
Note that some tags written in XML notation cannot be tested correctly; see the note for the assertResultEquals
function below.
assertTemplateEquals
self:assertTemplateEquals(expected, template, args, message)
This tests whether the first parameter equals a template call. The second parameter is the template name, and the third parameter is a table of the template arguments.
self:assertTemplateEquals(4, 'add', {2, 2}) -- true if {{add|2|2}} equals 4
Note that some tags written in XML notation cannot be tested correctly; see the note for the assertResultEquals
function below.
assertResultEquals
self:assertResultEquals(expected, text, message)
This tests whether the first parameter equals the expansion of any wikitext. The second parameter can be any wikitext.
self:assertResultEquals(4, '{{#invoke:Calculator|add|2|2}}')
Note that some special tags written in XML notation, such as <pre>
, <nowiki>
, <gallery>
and <ref>
cannot be compared correctly. These tags are converted to strip markers before they are processed by Lua. Strip markers are unique, even when generated from identical input, so any tests testing these tags for equality will fail. This also applies to the assertTemplateEquals
and assertSameResult
functions.
assertSameResult
self:assertSameResult(text1, text2, message)
This tests whether the expansion of a given string of wikitext equals the expansion of another string of wikitext. This can be useful for verifying that a module behaves in the same way as a template it is intended to replace.
self:assertSameResult('{{add|2|2}}', '{{#invoke:Calculator|add|2|2}}')
Note that some tags written in XML notation cannot be tested correctly; see the note for the assertResultEquals
function above.
assertThrows
self:assertThrows(fn, expectedMessage, message)
This tests whether a given function throws an exception. If expectedMessage
is not nil
, it will check that an exception was thrown with the given error message.
See also