Mochitest
DISCLAIMER
If you are testing web platform code, prefer using use a wpt test (preferably upstreamable ones).
Introduction
Mochitest is an automated testing framework built on top of the MochiKit JavaScript libraries.
Only things that can be tested using JavaScript (with chrome privileges!) can be tested with this framework. Given some creativity, that’s actually much more than you might first think, but it’s not possible to write Mochitest tests to directly test a non-scripted C++ component, for example. (Use a compiled-code test like GTest to do that.)
Running tests
To run a single test (perhaps a new test you just added) or a subset of the
entire Mochitest suite, pass a path parameter to the mach command.
For example, to run only the test test_CrossSiteXHR.html in the Mozilla source
tree, you would run this command:
./mach test dom/security/test/cors/test_CrossSiteXHR.html
To run all the tests in dom/svg/, this command would work:
./mach test dom/svg/
You can also pass a manifest path to run all tests on that manifest:
./mach test dom/base/test/mochitest.ini
Running flavors and subsuites
Flavors are variations of the default configuration used to run Mochitest. For example, a flavor might have a slightly different set of prefs set for it, a custom extension installed or even run in a completely different scope.
The Mochitest flavors are:
plain - The most basic and common Mochitest. They run in content scope, but can access certain privileged APIs with SpecialPowers.
browser - These often test the browser UI itself and run in browser window scope.
chrome - These run in chrome scope and are typically used for testing privileged JavaScript APIs. More information can be found here.
a11y - These test the accessibility interfaces. They can be found under the top
accessibledirectory and run in chrome scope. Note that these run without e10s / fission.
A subsuite is similar to a flavor, except that it has an identical configuration. It is just logically separated from the “default” subsuite for display purposes. For example, devtools is a subsuite of the browser flavor. There is no difference in how these two jobs are run. It exists so that the devtools team can easily see and run their tests.
Note: There are also tags, which are similar to subsuites. Although they both are used to logically group related sets of tests, they behave differently. For example, applying a subsuite to a test removes that test from the default set, whereas, a tag does not remove it.
By default, mach finds and runs every test in the given subdirectory no matter
which flavor or subsuite it belongs to. But sometimes, you might only want to
run a specific flavor or subsuite. This can be accomplished using the --flavor
(or -f) and --subsuite options respectively. For example:
./mach mochitest -f plain # runs all plain tests
./mach mochitest -f browser --subsuite devtools # runs all browser tests in the devtools subsuite
./mach mochitest -f chrome dom/indexedDB # runs all chrome tests in the dom/indexedDB subdirectory
In many cases, it won’t be necessary to filter by flavor or subsuite as running specific directories will do it implicitly. For example running:
./mach mochitest devtools/
Is a rough equivalent to running the devtools subsuite. There might be
situations where you might want to run tests that don’t belong to any subsuite.
To do this, use:
./mach mochitest --subsuite default
Debugging individual tests
If you need to debug an individual test, you could reload the page containing the test with the debugger attached. If attaching a debugger before the problem shows up is hard (for example, if the browser crashes as the test is loading), you can specify a debugger when you run mochitest:
./mach mochitest --debugger=gdb ...
See also the --debugger-args and --debugger-interactive arguments. You can
also use the --jsdebugger argument to debug JavaScript.
--no-autorun can be useful as well in that case, so that you can add
breakpoints or switch to the Netmonitor panel before starting the test.
Debug using the Firefox Profiler
Run the profiler with --profiler (both locally and on try). On try, only
failures will generate and upload profiles, so make sure to add an assertion that
fails the test you’re interested in.
Finding errors
Search for the string TEST-UNEXPECTED-FAIL to find unexpected failures. You
can also search for SimpleTest FINISHED to see the final test summary.
Logging results
The output from a test run can be sent to the console and/or a file (by default
the results are only displayed in the browser). There are several levels of
detail to choose from. The levels are DEBUG, INFO, WARNING, ERROR and
CRITICAL, where DEBUG produces the highest detail (everything), and
CRITICAL produces the least.
Mochitest uses structured logging. This means that you can use a set of command
line arguments to configure the log output. To log to stdout using the mach
formatter and log to a file in JSON format, you can use --log-mach=-
--log-raw=mochitest.log. By default the file logging level for all your
formatters is INFO but you can change this using --log-mach-level=<level>.
To turn on logging to the console use --console-level=<level>.
For example, to log test run output with the default (tbpl) formatter to the
file ~/mochitest.log at DEBUG level detail you would use:
./mach mochitest --log-tbpl=~/mochitest.log --log-tbpl-level=DEBUG
Headless mode
The tests must run in a focused window, which effectively prevents any other
user activity on the engaged computer. You can avoid this by using the
--headless argument or MOZ_HEADLESS=1 environment variable.
./mach mochitest --headless ...
Writing tests
A Mochitest plain test is simply an HTML or XHTML file that contains some JavaScript to test for some condition.
Asynchronous Tests
Sometimes tests involve asynchronous patterns, such as waiting for events or
observers. In these cases, you need to use add_task:
add_task(async function my_test() {
let keypress = new Promise(...);
// .. simulate keypress
await keypress;
// .. run test
});
Use add_setup() when asynchronous test task is meant to prepare test for run.
All setup tasks are executed once in order they appear prior to any test tasks.
add_setup(async () => {
await clearStorage();
});
Or alternatively, manually call waitForExplicitFinish and finish:
SimpleTest.waitForExplicitFinish();
addEventListener("keypress", function() {
// ... run test ...
SimpleTest.finish();
}, false);
// ... simulate key press ...
If you need more time, requestLongerTimeout(number) can be quite useful.
requestLongerTimeout() takes an integer factor that is a multiplier for the
default 45 seconds timeout. So a factor of 2 means: “Wait for at last 90s
(2*45s)”. This is really useful if you want to pause execution to do a little
debugging.
Test functions
Each test must contain some JavaScript that will run and tell Mochitest whether
the test has passed or failed. SimpleTest.js provides a number of functions
for the test to use, to communicate the results back to Mochitest. These
include:
ok(expressionThatShouldBeTrue, "Description of the check")– tests a value for its truthfulnessis(actualValue, expectedValue, "Description of the check")– compares two values (using Object.is)isnot(actualValue, unexpectedValue, "Description of the check")– opposite of is()
If you want to include a test for something that currently fails, don’t just comment it out! Instead, use one of the “todo” equivalents so we notice if it suddenly starts passing (at which point the test can be re-enabled):
todo(falseButShouldBeTrue, "Description of the check")todo_is(actualValue, expectedValue, "Description of the check")todo_isnot(actualValue, unexpectedValue, "Description of the check")
Tests can call a function info("Message string") to write a message to the
test log.
In addition to mochitest assertions, mochitest supports the
CommonJS standard assertions,
like Node.JS’ Assert module but
implemented in Assert.sys.mjs. These are auto-imported in the browser flavor, but
need to be imported manually in other flavors.
It is recommended to use Assert methods over is/ok functions, because
they are better documented and easier to find.
Helper functions
Right now, useful helpers derived from MochiKit are available in
testing/mochitest/tests/SimpleTest/SimpleTest.js.
Although all of Mochikit is available at testing/mochitest/MochiKit, only
include files that you require to minimize test load times. Bug 367569 added
sendChar, sendKey, and sendString helpers.
These are available in