Developer log #8 :: MWUnit
MWUnit is an extension for MediaWiki that provides a unit testing framework for templates, parser functions and other wikitext programming features. It allows people who write technical templates to programmatically test those templates to ensure that a template meets its design requirements and behaves as intended.
The MWUnit extension was designed to be an instance of the xUnit unit testing framework architecture. This means it consists of the following basic components:
- A test runner - runs tests and reports the test results;
- A test case - elemental specification of inputs, testing procedure and expected results;
- A test fixture - the set of preconditions or state needed to run a test;
- A test suite - a set of tests that share the same fixture;
- Assertions - a function that verifies that its input is of an expected form.
The extension defines a large number of assertions. These assertions are in the form of parser functions and test, as mentioned above, whether the given value conforms to the assertion. Some important assertions are:
#assert_equals
- Reports an error if and only if the two values given are not identical;#assert_empty
- Reports an error if and only if the given value is not empty;#assert_that
- Reports an error if and only if the given value is nottrue
,yes
,on
or1
;#assert_expression
- Reports an error if the given boolean expression evaluates tofalse
.
The full list of assertions with more extensive documentation can be found on the extension page.
Testing the fictional Multiply template
To make the above much clearer, I will explain the extension through a worked out example involving the fictional Multiply template. The template has the following source:
{{#expr: {{{1|0}}} * {{{2|0}}} }}
The template multiplies the given variables together. If a variable is missing, it will return zero. Before we write the test cases, it is a good idea to think about how we expect the template to behave. We should think of the edge cases and the template's regular behaviour. Let's write down some of the test cases we can think of:
{{Multiply|1|1}}
: should return1
{{Multiply|1}}
: should return0
{{Multiply}}
: should return0
{{Multiply|1|x}}
: should returnx
{{Multiply|0|x}}
: should return0
{{Multiply|x|y}}
: should return{{Multiply|y|x}}
{{Multiply|foo|2}}
: should return an error{{Multiply|2|foo}}
: should return an error{{Multiply|foo|bar}}
: should return an error
Now that we have a rudimentary specification of the template, we can create a test suite for it. The test suite does not require a fixture, since no global state is used for testing this template. We will start by creating the page Test:Multiply
and adding a test case to it. The first test case will look something like this:
<testcase name="testMultiplyOneByOneEqualsOne" group="Multiply tests"> {{#assert_equals: 1 | {{Multiply|1|1}} }} </testcase>
Running the test suite tells us that our test case passed:
The entire test suite will look something like this:
<testcase name="testMultiplyOneByOneEqualsOne" group="Multiply tests"> {{#assert_equals: 1 | {{Multiply|1|1}} }} </testcase> <testcase name="testMultiplyMissingSecondArgumentEqualsZero" group="Multiply tests"> {{#assert_equals: 0 | {{Multiply|1}} }} </testcase> <testcase name="testMultiplyMissingBothArgumentsEqualsZero" group="Multiply tests"> {{#assert_equals: 0 | {{Multiply}} }} </testcase> <testcase name="testMultiplyingByOneRemainsOriginal" group="Multiply tests"> {{#loop: x | 0 | 100 | {{#assert_equals: {{#var: x}} | {{Multiply|1|{{#var: x}} }} }} }} </testcase> <testcase name="testMultiplyingByZeroEqualsZero" group="Multiply tests"> {{#loop: x | 0 | 100 | {{#assert_equals: 0 | {{Multiply|0|{{#var: x}} }} }} }} </testcase> <testcase name="testMultiplyIsCommutative" group="Multiply tests"> {{#loop: x | 0 | 100 | {{#loop: y | 0 | 100 | {{#assert_equals: {{Multiply|{{#var: x}}|{{#var: y}} }} | {{Multiply|{{#var: y}}|{{#var: x}} }} }} }} }} </testcase> <testcase name="testMultiplyWithInvalidParameterReturnsError" group="Multiply tests"> {{#assert_error: {{Multiply|foo|2}} }} {{#assert_error: {{Multiply|2|foo}} }} {{#assert_error: {{Multiply|foo|bar}} }} </testcase>
You have now tested your first ever template and learned the basics of MWUnit. If you would like to learn more about the many other things MWUnit has to offer, such as annotations, mocks and the command-line test runner, head over to the extension page.