株式会社クリアコード > フリーソフトウェア開発 > UxU - UnitTest.XUL > How to write testcases for UxU?

How to write testcases for UxU? UxU - UnitTest.XUL

Basic rules of testcases

UxU recognizes and runs JavaScript files written in some rules, as testcases.

Testcase files should be named like "<TEST'S NAME>.test.js". When UxU collects testcases from specified folders recursively, only "*.test.js" files will be done if there are both "*.js" and "*.test.js" files. (However, when there are only "*.js" files, UxU tries to parse all of them as testcase.)

In the execution context of codes in testcases, many helper methods introduced by UxU are available. See the list of helper methods.

You can see and try actual testcases written for UxU. There are some testcases which test UxU itself, in the source code tree. Let's checkout UxU's Subversion repository and run tests in tests/uxu/.

Simple testcases (unit-tests)

This is an example. Each file becomes one "testcase".

var description = 'Description of this testcase';

function setUp() {
  // This function is always processed before each test.
  // (ex. creating instances of the class now tested, etc.)
}

function tearDown() {
  // This function is always processed after each test.
  // (ex. destroying instances, etc.)
}

function warmUp()
{
  // This function is processed only once before tests.
  // (ex. loading the class you want to test now, etc.)
}

function coolDown()
{
  // This function is processed only once after all tests finish.
}

testWillSuccess.description = 'Successful test';
testWillSuccess.priority    = 'normal';
function testWillSuccess() {
  assert.equals(0, [].length);
  assert.notEquals(10, ''.length);
  assert.isTrue(true);
  assert.isFalse(false);
  assert.isDefined(assert);
  assert.isUndefined(void(0));
  assert.isNull(null);
  assert.raises('TypeError', (function() { null.property = true; }), this);
  assert.matches(/patterns?/, 'pattern');
}

testWillFail.description = 'Failure test';
testWillFail.priority    = 'low';
function testWillFail() {
  assert.isTrue(false);
}

In the global namespace, some functions which have one of special names are recognized as "testcase function". Functions can be recognized not only by names, but by their property.

Names of functions which are recognized as special functions for the testcase
Name (begins from)How works?Specifying by properties
setUp... Operations to set up each test function.isSetUp = true;
tearDown... Opeartions to tear down each test function.isTearDown = true;
warmUp... Operations to set up the whole testcase at first function.isWarmUp = true;
coolDown... Operations to tear down the whole testcase on the end function.isCoolDown = true;
test... Each test function.isTest = true;

They are processed in the order like this:

  1. codes written on the global namespace
  2. warmUp (if exists)
  3. setUp (if exists)
  4. the first test
  5. tearDown (if exists)
  6. setUp (if exists)
  7. the second test
  8. tearDown (if exists)
  9. ...snip...
  10. setUp (if exists)
  11. the Nth (final) test
  12. tearDown (if exists)
  13. coolDown (if exists)

However it is not assured that tests are done in their defined order. The order is possibly shuffled.

The name of the testcase itself or descriptions can be supplied by a global variable, description. It will be shown in the test runner GUI.

Compatibility for MozUnit(MozLab)'s testcases

Testcases written for MozUnit (a part of MozLab) are also available.

Differently from MozUnit, UxU ignores runStrategy option. You don't have to use the option anymore because UxU is designed to run asynchronous tests by default. (But, for compatibility, you can get and call the continuation function as the argument in setUp functions.)

Complex testcases (functional-tests) with deferred operations

Tests written for UxU can do deferred operations easily by utils.wait() or yield. (utils.wait() is available on Firefox 3/Thunderbid 3 and later. You should use it if you write tests only for Firefox 3/Thunderbird 3 and later. When you wish to support Firefox 2/Thunderbird 2, use yield instead.)

When a utils.wait() or an yield appear in tests, setUp, tearDown, warmUp, or coolDown, then UxU suspends the test. And, after some conditions are met, he resumes from the next statement of the utils.wait() (or yield). The condition for resuming is changed by the type of the specified value.

Available types of the value for utils.wait()/yield and conditions for resuming
TypeDescriptions and examples
Numeric value

Specified value is recognized as a wait time in milliseconds.

var beforeTime = (new Date()).getTime();
utils.wait(1000); // Wait one second (equals to 1000 milliseconds) and resume.
// yield 1000; // Wait one second (equals to 1000 milliseconds) and resume.
var afterTime = (new Date()).getTime();
assert.isTrue((afterTime - beforeTime) > 500);
assert.isTrue((afterTime - beforeTime) < 1500);
Object which has the value property

UxU watches the value of the object's value property. While the value is false, he keeps himself suspended. When the value is set to true, he resumes automatically. Timeout: after 30 seconds.

var browser;
functionalTest.tests = {
  setUp : function() {
    var loaded = { value : false };
    browser = window.openDialog(
            'chrome://browser/content/browser.xul');
    browser.addEventListener('load', function() {
        loaded.value = true;
    }, false);
    // Wait until the flag is set to true.
    utils.wait(loaded);
    // yield loaded;
    browser.gFindBar.openFindBar();
  },
  tearDown : function() {
    browser.close();
  },
  ...
};
Function(*)

UxU watches the returned value of the function. While the function returns false, he keeps himself suspended. When it returns true, he resumes automatically. Timeout: after 30 seconds.

var win = utils.getTestWindw();
var url = win.content.location.href;
// Wait that the page is correctly loaded.
var f = function() {
        return url != win.content.location.href
    };
utils.wait(f);
// yield f;
assert.equals(uri_to_be_redirected,
              win.content.location.href);
Generator-iterator(*)

UxU does iteration for the generator-iterator again and again, until StopIteration exception raises.

// Wait and go
function assert_page_load(aURI) {
  yield utils.loadURI(aURI);
  var win = utils.getTestWindow();
  assert.equals(aURI, win.content.location.href);
}

function assert_window_close() {
  var win = utils.getTestWindow();
  win.close();
  yield 500;
  assert.isTrue(win.closed);
}

utils.wait(assert_page_load('http://www.clear-code.com/'));
// yield assert_page_load('http://www.clear-code.com/');
utils.wait(assert_window_close());
// yield assert_window_close();
Generator(*) UxU creates generator-iterator from the generator automatically, and does it just like as cases for generator-iterators.
function assert_window_close() {
  ...
}

yield assert_window_close; // without "()"
Others If unknown type object is specified, UxU reports an error and stops test. If it is an object without value property, or a negative number, then UxU reports an error and stops test. Otherwise, the specified value will be converted to a number by Number(). If the result is not a nuber, then 0 will be used.

*Note: before specifying functions or generators for "yield" keyword

This is a note for yield. You don't have to mind it if you use utils.wait() instead.

yield possibly breaks the stack trace when an exception raises from inside of the function handed to yield.

// ex. test of mapping
function assertRedirect(aOriginalURI, aToBeRedirectedTo) {
  yield utils.loadURI(aOriginalURI);
  assert.equals(aToBeRedirectedTo, browser.currentURI.spec);
}
yield assertRedirect('http://myhost/page1',
                     'http://myhost/page1_redirected');
yield assertRedirect('http://myhost/page2',
                     'http://myhost/page2_redirected');
yield assertRedirect('http://myhost/page3',
                     'http://myhost/page3_redirected');

For example, the test above process three assertions, but, even if one of them failed, we cannot know which one failed because the stack-trace ends at the line assert.equals() exists.

A special helper function, Do() solves this problem. Wraping returned value of yield keyword by the function always creates a new stack, so you can detect the line which the exception really raised from.

yield Do(assertRedirect('http://myhost/page1',
                     'http://myhost/page1_redirected'));
yield Do(assertRedirect('http://myhost/page2',
                     'http://myhost/page2_redirected'));
yield Do(assertRedirect('http://myhost/page3',
                     'http://myhost/page3_redirected'));

By the way, Do() does nothing and returns the handed object as-is if it is not a function, generator, or generator-iterator. It solves the problem without any harmful effect. You should write Do() for every yield in your testcases.

How to assert the number of processed assertions?

You can specify how many assertions should be processed for each test, by assertions property.

testRoop.assertions = 10;
function testRoop() {
  for (var i = 0, maxi = data.length; i < maxi; i++)
  {
    assert.equals(expected[i], data[i], i);
  }
}

UxU will report the result as "failed", if the total number of processed assertions is different from the number specified by assertions property, even if there is no failed assertion. This feature will be useful to prevent unexpectedly successes of tests which are in developing. For example, a test will fail and you can realize that you've forgotten to remove comments including assertions, if you specify the number of assertions.

Verification of the number of processed assertions works for assertions written in each test function. Assertions in setUp/tearDown are ignored.

Even if you don't specify the number of assertions, UxU regards them as "success" but reports an warning, for tests which have no processed assertion.

minAssertions, maxAssertions

Additionally, there are two more features, minAssertions property (specifies minimum number of assertions which should succeed) and maxAssertions property (specifies maximum number of assertions which should succeed). They are available like as assertions, and you can use them in same time.

How to run testcases in clean or ready-made profile?

You can run the testcase with a Firefox (or Thunderbird) profile which is made for testing, not only the current profile.

var profile = '../profiles/test-profile/';

function setUp() {
...

The path to the profile for the testcase is supplied by a global variable, profile. If profile is defined and the folder is available, then UxU automatically starts child process of Firefox (or Thunderbird) with the profile and run the testcase in the environment.

The folder will be cloned as a temporary profile, so, the original files are not changed.

var profile = '../profiles/test-profile/';
var application = 'C:\\Program Files\\Mozilla Thunderbird\\thunderbird.exe';

...

If you speficy the path to the executable by a global variable application, UxU starts the specified application with the profile. (*However, application is ignored if you don't define profile.)

How to write data-driven tests?

UxU supports data-driven tests. If you want to do a test for various patterns, you can specify parameters for the test by parameters property, as an array or a hash.

For example, you maybe write a test for a feature which works about webpages, like:

function testMyFunction() {
  utils.wait(utils.loadURI('http://...'));
  assert.equals('result1', myFunc(utils.content));

  utils.wait(utils.loadURI('http://...'));
  assert.equals('result2', myFunc(utils.content));

  utils.wait(utils.loadURI('http://...'));
  assert.equals('result3', myFunc(utils.content));

  ...
}

However, this test will annoy you. You have to copy and paste same codes for new patterns, so, the test will become too long. If the third assertion fails, then all following assertions will be skipped. OK, you'll correct a bug, and redo the test. Then, the fourth assertion fails and followings are skipped again. You have to fix a bug and do the test, again and again. That is too annoying!

You should write those tests in data-driven style. See:

testMyFunction.parameters = [
  { uri: 'http://...', expected: 'result1' },
  { uri: 'http://...', expected: 'result2' },
  { uri: 'http://...', expected: 'result3' },
  ...
];
function testMyFunction(aParameter) {
  utils.wait(utils.loadURI(aParameter.uri));
  assert.equals(aParameter.expected, myFunc(utils.content));
}

If you specify parameters for the test (test function's parameters property) as an array, then, UxU runs the test function for each items of the array. The parameter will be handed as the first argument of the function. (If you specified private setUp or tearDown for the test, they also receive parameter same to the test function itself.) UxU runs the test each time as stand-alone test, with unique name like "testMyFunction (1)", "testMyFunction (2)", "testMyFunction (3)", etc. All tests will be done even if one of them failed.

If you specify parameters as a hash, UxU uses each item of the hash as parameters.

testMyFunction.parameters = {
  google: { uri: 'http://...', expected: 'result1' },
  yahoo:  { uri: 'http://...', expected: 'result2' },
  mixi:   { uri: 'http://...', expected: 'result3' },
  ...
};
function testMyFunction(aParameter) {
  utils.wait(utils.loadURI(aParameter.uri));
  assert.equals(aParameter.expected, myFunc(utils.content));
}

In this case, each test will be named with keys of the hash like "testMyFunction (google)", "testMyFunction (yahoo)", "testMyFunction (mixi)", etc, so you'll easily be able to find out the pattern which causes problem.

To assist your development of data-driven tests, there is a useful helper function which generates hash or array from external CSV files. You can write and maintain testcases which include many many patterns, by OpenOffice.org Calc, Microsoft Excel, etc.

Skipping of tests by conditions

Priorities

You can specify priority of each test, by priority property. For example:

testBasic.priority = 'must';
function testBasic() {
  // This is the test for basic features.
  // They are very important, so must be tested anytime.
  assert.equals(3, calcService.add(1, 2));
  ...
}

testMinor.priority = 'low';
function testMinor() {
  // This is the test for a minor feature.
  // We don't need to test the feature frequently.
  ...
}

testUnderConstruction.priority = 'never';
function testUnderConstruction() {
  // This test always fails because the feature is under construction.
  // We cannot clearly know that other tests succeed or failed.
  // So we should turn off this test before the feature is implemented.
  ...
}

UxU is designed and configured for test-driven development by default. In this development style, you'll run tests again and again. Tests which succeed in the last time often success in the next, so they don't have to be processed every time. So, UxU often skips tests which succeed in the last time, but always runs newly added or failed test anyway. By this feature, each testing-time shortens and you can run tests many times without stresses.

Test's priority is specified in a number, or a string listed below:

Priorities of tests
PriorityProbabilityNumeric style
must 100% 1
important 90% 0.9
high 70% 0.7
normal (by default) 50% 0.5
low 25% 0.25
never 0% 0

By the way, you can specify global priority of the test case by the global variable proprity. UxU applies global priority for each test which has no personal priority.

var priority = 'must';

testFoo.priority = 'never';
function testFoo() { ... } // this will be skipped.

function testBar() { ... } // this will be performed.

Target applications

You can forbid to run Firefox-specific testcases on Thunderbird.

var targetProduct = 'Thunderbird';

function setUp() {
...

If you specify the application name by a global variable targetProduct, UxU skips to run the testcase if the current application isn't it. This feature is useful for addons which works on both Firefox and Thunderbird.

Additionally, if you specify profile in same time, UxU runs the testcase on the application automatically.

You can specify the target application for each test by their targetProduct property. This is useful if your extension works on both Firefox and Thunderbird, and you have to write application-specific tests.

testForFirefox.targetProduct = 'Firefox';
function testForFirefoxSpecificFeature() { ... }

testMinor.targetProduct = 'Thunderbird';
function testForThunderbirdSpecificFeature() { ... }

function testForAllApplication() { ... }

Moreover, you can specify the target application for whole of the test case by the global variable targetProduct. If you specify it, UxU determines that he should skip the test case or not at the startup process. Then, tests which have other applications as their targetProduct will be skipped.

var targetProduct = 'Thunderbird';

testA.targetProduct = 'Firefox';
function testA() { ... } // this will be skipped.

function testB() { ... } // this will be performed.

If you specify both global targetProduct and the profile, UxU automatically runs the test case on the application with the profile.

Other conditions

You can skip each test by other conditions, specifying shouldSkip property of each tests. If you specify a function to shouldSkip property, it will be evaluated just before the test is performed (before setUp() is performed) and UxU skips the test if the returned value is false. Other cases, UxU evaluates the value of shouldSkip property as a boolean.

testA.shouldSkip = true;
function testA() { /* skip this, because this is under construction. */ }

// don't run after 2009-02-09
testB.shouldSkip = function() {
  return Date.now() > (new Date('2009/2/9')).getTime();
};
function testB() { ... }

If you wish to skip whole of the test case, specify the global variable shouldSkip. When the value (or returned value) of shouldSkip is true, UxU completely ignores all of contents of the test case.

var shoudSkip = true;

// they are always skipped!
testA.priority = 'must';
function testA() { ... }
function testB() { ... }

Mapping for URIs

*For compatiblity for other addons, this feature is disabled by default. If you wish to use it, go to UxU's configuration and turn the checkbox "Enable URI mapping for testcases" on.

For testing of a module which is designed to load resources from fixed URIs, UxU provides an simple mapping feature. For example, you can hand contents of a local file "012345.xml" to your module by loading an URI "http://myservice.example.com/myapi?userid=012345".

var mapping = {
      '*.google.*/*' : baseURL+'../fixtures/google-pages.html',
      '*.yahoo.*/*'  : baseURL+'../fixtures/yahoo-pages.html',
      'https://addons.mozilla.org/ja/firefox/addon/*'
                     : baseURL+'../fixtures/addons/$1.html'
    };

function setUp() { ... }
function tearDown() { ... }
function test_loadInBackground() {
  myModule.loadURL('https://addons.mozilla.org/ja/firefox/addon/6357');
  utils.wait(function() { return myModule.loaded; });
  assert.equals('summary of the loaded document', myModule.getSummary());
}

Like above, mapping rules for the testcase is supplied by a global variable, mapping. If mapping is defined, then any request (including subframes, XMLHttpRequest, and so on) for URIs matching to the rules will be mapped.

UxU's mapping works like replacing of contents by a proxy. In other words, UxU works as a local proxy. When your requesting is mapped, you will get the contents of the document from the URI after mapping, however the response URI will be still the original URI.

Styles of mapping rule(s)
StyleDescriptions and examples
Hash variable (associative array, object literals)

Keys of the hash are rules to detect mapping sources, and values are mapping targets. Two wildcards, "*" (matches to any number of characters or blank) and "?" (matches a character).

var mapping = {
      'http://release.example.com/'  : baseURL+'../fixtures/home.html',
      'http://release.example.com/*' : 'http://test.example.com/$1',
      'http://www??.example.com/*'   : 'http://test.example.com/server$1$2/$3'
    };
Array

Elements of odd indexes are rules to detect mapping sources, and even indexes are mapping targets. Two wildcards, "*" (matches to any number of characters or blank) and "?" (matches a character). Moreover, you can use regular expressions for mapping sources.

var mapping = [
      'http://release.example.com/'      , baseURL+'../fixtures/home.html',
      'http://release.example.com/*'     , 'http://test.example.com/$1',
      /^http://www(\d+).example.com/(.*)', 'http://test.example.com/server$1/$2'
    ];
Function

The function will receive the original URI as an nsIURI object, and have to return a new URI as a string. If the function returns the original URI, blank string, false, or null, UxU doesn't map the request.

var mapping = function(aOriginalURI) {
      return aOriginalURI.spec.indexOf('google') > -1 ?
               baseURL+'../fixtures/google-pages.html' :
               null ;
    };

*In old versions of UxU, redirect was used for the feature, instead of mapping. redirect is still available on UxU 0.7.6 and later for backward compatibility.

Assertions

This is the list of available assertions.

Assertions for values

void assert.equals(in Object aExpected, in Object aActual, [in String aMessage]) alias: assertEqual()

Compares the expected and the actual, and AssertionFailed exception raises if they are not equal unexpectedly. They are compared by the == operator. This works for primitives, objects, DOM nodes, and arrays.

assert.equals('expectedValue', object.property);
assert.equals($('node'), object.returnsNode());
// this compares each element in both arrays.
assert.equals([$('node1'), $('node2'), $('node2')],
              object.returnsNodesArray());
// this compares each value of properties of both objects.
assert.equals({ name : 'foo', value : 'bar' },
              object.getResult());
void assert.strictlyEquals(in Object aExpected, in Object aActual, [in String aMessage]) alias: assertStrictlyEqual()

This is a strict version of assert.equals(). Values are compared by the === operator.

var value = 'expectedValue';
assert.strictlyEquals('expectedValue', value); // success
value = new String('expectedValue');
assert.strictlyEquals('expectedValue', value); // failure
void assert.notEquals(in Object aExpected, in Object aActual, [in String aMessage]) alias: assertNotEqual()

Opposite of assert.equals(). Compares the expected and the actual, and AssertionFailed exception raises if they are unexpectedly equal.

assert.notEquals('true', node.getAttribute('disabled'));
assert.notEquals(object.firstItem, object.lastItem);
void assert.notStrictlyEquals(in Object aExpected, in Object aActual, [in String aMessage]) alias: assertNotStrictlyEqual()

Opposite of assert.strictlyEquals(). Values are compared by the !== operator.

var value = '10';
assert.notStrictlyEquals(10, value); // success
assert.notStrictlyEquals(10, parseInt(value)); // failure
void assert.contains(in Object aElementShouldBeContained, in Object aElementsOrRange, [in String aMessage]) alias: assertContain()

Asserts that the second argument contains the first argument. AssertionFailed exception raises if the second doesn't contain the first.

You can specify a nsISelection, a DOM Range, a DOM Node, or an array as the second argument. Otherwise this method converts the object to a string. See specific examples:

second argumentfirst argumentassertion
nsISelection DOM Range The range of the nsISelection completely wraps the first argument range. (Note: This doesn't assert that one of ranges of the nsISelection equals the first argument. This only asserts that one of them wraps the first argument.)
DOM Node The range of nsISelection completely wraps the node.
DOM Range DOM Range The second argument range completely wraps the first argument range.
DOM Node The range completely wraps the node.
DOM Node DOM Range The whole range of the node's sub tree wraps the first argument range.
DOM Node The first argument node is a descendant of the second argument node.
Array Any object The object is an element of the array.
Otherwise The stringized version of the second argument object contains the stringized version of the first argument object. (Note: This is case sensitive.)
assert.contains('- Firefox', window.document.title);

var terms = ['extracted', 'extended', 'expected'];
assert.contains('expected', terms); // success

assert.contains(doc.body.firstChild,
                doc.body); // success

var range = doc.createRange();
range.selectNode(doc.body);
assert.contains(doc.body, range); // success
assert.contains(doc.body.firstChild, range); // success
assert.contains(doc.documentElement, range); // failure
void assert.notContains(in Object aElementShouldNotBeContained, in Object aElementsOrRange, [in String aMessage]) alias: assertNotContain()

Opposite of assert.contains().

assert.notContains('error', object.getResultsArray());
assert.notContains('out of selection', content.getSelection());
void assert.contained(in Object aElementsOrRangeShouldContain, in Object aElement, [in String aMessage]) alias: assertContained()

Asserts that the second argument is contained in the first argument. AssertionFailed exception raises if the first doesn't contain the second.

You can specify a nsISelection, a DOM Range, a DOM Node, or an array as the first argument. Otherwise this method converts the object to a string. See assert.contains() also.

assert.contained('Start - Firefox', myAddon.brandName);

var terms = ['extracted', 'extended', 'expected'];
assert.contained(terms, myAddon.lastTerm);

assert.contained(doc.body,
                 doc.body.firstChild); // success

var range = doc.createRange();
range.selectNode(doc.body);
assert.contained(range, doc.body.firstChild); // success
assert.contained(range, doc.documentElement); // failure
void assert.notContained(in Object aElementsOrRangeShouldNotContain, in Object aElement, [in String aMessage]) alias: assertNotContaied()

Opposite of assert.contained().

assert.notContained('error,failure', myAddon.lastStatus);
assert.notContained(content.getSelection(), myAddon.getNextNodeOfSelection());
void assert.isTrue(in aExpression, [in String aMessage]) alias: assertTrue()

If the expression equals to false (ex. 0, "", false, null, or undefined) unexpectedly, then AssertionFailed exception raise.

checkbox.setAttribute('checked', true);
assert.isTrue(checkbox.checked);
void assert.isFalse(in aExpression, [in String aMessage]) alias: assertFalse()

If the expression equals to true unexpectedly, then AssertionFailed exception raises.

checkbox.removeAttribute('checked');
assert.isTrue(checkbox.checked);
void assert.inDelta(in Number aExpected, in Number aActual, in Number aDelta, [in String aMessage]) alias: assertInDelta()

AssertionFailed exception raises if the actual value is out of the specified range. In other words, this fails when "aActual < aExpected - aDelta || aActual > aExpected + aDelta" is true.

var array = '0,1,2,3,4,5,6,7,8,9'.split(',');
// extract about half elements at random
var extracted = array.filter(function(aItem) {
		return Math.random() > 0.5;
	});
// assert the number of extracted items,
// margin of error is plus or minus 2
assert.inDelta(5, extracted.length, 2);
void assert.compare(in Number aExpected, in String aOperator, in Number aActual, [in String aMessage]) alias: assertCompare()

AssertionFailed exception raises if the result of comparison aExpected and aActual by aOperator is false. Available operators are ==, ===, !=, !==, <, <=, >, and >=. Otherwise raises an error

assert.compare(100, '<=', func1());
assert.compare(100, '>', func2());

Note: special cases of "equals" assertions

assert.equals(), assert.notEquals(), assert.strictlyEquals(), assert.notStrictlyEquals(), and assert.compare() work specially for the following types:

Date

Date-times are compared.

Array

Lengthes and all elements are compared.

Object (object literals, hashes, instances of custom classes)

Names and values of all properties are compared.

Assertions for the type of the value

void assert.isBoolean(in aExpression, [in String aMessage]) alias: assertBoolean()

AssertionFailed exception raises if it is not a boolean value unexpectedly.

assert.isBoolean(checkbox.checked);
assert.isBoolean(checkbox.disabled);
assert.isBoolean(utility.getPref('some.boolean.pref'));
void assert.isNotBoolean(in aExpression, [in String aMessage]) alias: assertNotBoolean()

AssertionFailed exception raises if it is a boolean value unexpectedly.

assert.isNotBoolean(notInitializedObject.booleanProperty);
void assert.isString(in aExpression, [in String aMessage]) alias: assertString()

AssertionFailed exception raises if it is not a string value unexpectedly.

assert.isString(input.value);
assert.isString(element.getAttribute('undefined-attribute'));
assert.isString(utility.getPref('some.string.pref'));
void assert.isNotString(in aExpression, [in String aMessage]) alias: assertNotString()

AssertionFailed exception raises if it is a string value unexpectedly.

assert.isNotString(notInitializedObject.stringProperty);
void assert.isNumber(in aExpression, [in String aMessage]) alias: assertNumber()

AssertionFailed exception raises if it is not a numeric value unexpectedly.

assert.isNumber(httpClient.errorCode);
assert.isNumber(utility.getPref('some.integer.pref'));
void assert.isNotNumber(in aExpression, [in String aMessage]) alias: assertNotNumber()

AssertionFailed exception raises if it is a numeric value unexpectedly.

assert.isNotString(notInitializedObject.intProperty);
void assert.isFunction(in aExpression, [in String aMessage]) alias: assertFunction()

AssertionFailed exception raises if it is not a function unexpectedly.

assert.isFunction(element.hasAttribute);
assert.isFunction(instance.inheritedMethod);
void assert.isNotFunction(in aExpression, [in String aMessage]) alias: assertNotFunction()

AssertionFailed exception raises if it is a function unexpectedly.

assert.isNotFunction(instanceOfSuperClass.methodOfSubClass);
void assert.isDefined(in aExpression, [in String aMessage]) alias: assertDefined()

AssertionFailed exception raises if it is undefined unexpectedly.

assert.isDefined(instance.inheritedProperty);
assert.isDefined(Ci.myICustomInterface);
void assert.isUndefined(in aExpression, [in String aMessage]) alias: assertUndefined()

AssertionFailed exception raises if it is defined unexpectedly.

assert.isUndefined(checkbox.valid);
assert.isUndefined(Ci.myIOldCustomInterface);
void assert.isNull(in aExpression, [in String aMessage]) alias: assertNull()

AssertionFailed exception raises if it is not null unexpectedly.

assert.isNull(element.nodeValue);
assert.isNull(utility.getPref('some.undefined.pref'));
void assert.isNotNull(in aExpression, [in String aMessage]) alias: assertNotNull()

AssertionFailed exception raises if it is null unexpectedly.

assert.isNotNull(element.getAttribute('undefined-attribute'));
assert.isNotNull(utility.getPref('some.defined.pref'));
void assert.inplementsInterface(in aInterface, in aObject, [in String aMessage]) alias: assert.inplementInterface(), assertInplementsInterface(), assertInplementInterface()

Asserts an object inplements an XPCOM interface. You can specify any interface by the reference (like Components.interfaces.nsI*) or the name of the interface (like "nsI*).

AssertionFailed exception raises if the object doesn't implement the specified interface.

assert.implementsInterface(Ci.nsIDOMWindow, getWindow());
assert.implementsInterface('nsIDOMWindow', getWindow());
assert.implementsInterface(Ci.nsIDOMRange, getWindow()); // this will fail

Assertions for the raising exception

Object assert.raises(in Exception aExpected, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assertRaise()

This runs the function in the scope specified as the third argument or the current scope. If no exception or unexpected one raises from the operation, then AssertionFailed exception raises.

loader.INVALID_URI_ERROR = new Error('invalid uri');
loader.open = function(aURI) {
  if (aURI) this.uri = aURI;
  if (!/^https?:/.test(this.uri))
    throw this.INVALID_URI_ERROR;
  window.open(this.uri);
};
loader.uri = 'mailto:test@example.com';

assert.raises(
  loader.INVALID_URI_ERROR,
  loader.open,
  loader
);
assert.raises(
  loader.INVALID_URI_ERROR,
  function() {
    loader.open('ftp://ftp.example.com');
  },
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

utils.wait(assert.raises(
    loader.INVALID_URI_ERROR,
    function() {
      loader.open('http://www.example.com');
      utils.wait(500);
      loader.open('ftp://ftp.example.com');
    },
    {}
  ));
Object assert.notRaises(in Exception aExpected, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assertNotRaise()

Opposite of assert.raises(). If the specified exception raises from the operation, then AssertionFailed exception raises.

loader.INVALID_URI_ERROR = new Error('invalid uri');
loader.open = function(aURI) {
  if (aURI) this.uri = aURI;
  if (!/^https?:/.test(this.uri))
    throw this.INVALID_URI_ERROR;
  window.open(this.uri);
};
loader.uri = 'http://www.example.com';

assert.notRaises(
  loader.INVALID_URI_ERROR,
  loader.open,
  loader
);
assert.notRaises(
  loader.INVALID_URI_ERROR,
  function() {
    loader.open('https://secure.example.com');
  },
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

utils.wait(assert.notRaises(
    loader.INVALID_URI_ERROR,
    function() {
      loader.open('http://www1.example.com');
      utils.wait(500);
      loader.open('http://www2.example.com');
    },
    {}
  ));

Assertions for amount of time required

Object assert.finishesWithin(in Number aExpectedTime, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assert.finishWithin(), assertFinishWithin()

This runs the function in the scope specified as the third argument or the current scope. If it uses more than the expected time (in milliseconds), then AssertionFailed exception raises.

function functionContainsLongLoop() {
  for (var i = 0, maxi = 5000; i < maxi; i++) {
    process(data[i]);
  }
}
assert.finishesWithin(
  5 * 1000, // 5sec.
  functionContainsLongLoop,
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

var manager = {
    tasks : [],
    func  : loadAndParse,
    timer : null,
    start : function() {
      this.timer = window.setInterval(function(aSelf) {
          if (!aSelf.tasks.length) {
            window.clearInterval(aSelf.timer);
            return;
          }
          aSelf.func(aSelf.tasks.shift());
        }, 10, this);
    }
  };
manager.tasks.push('http://www.example.com/');
manager.tasks.push('http://www.example.jp/');
manager.tasks.push('http://www.example.net/');

utils.wait(assert.finishesWithin(
    10 * 1000, // 10sec.
    function() {
      manager.start();
      while (manager.tasks.length) {
        utils.wait(100);
      }
    },
    {}
  ));

Assertions for the number of performed assertions

Object assert.assertionsCountEquals(in Number aExpectedCount, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assert.assertionsCountEqual(), assertAssertionsCountEqual()

This is the local version of assertion by assertions property.

This runs the function in the scope specified as the third argument or the current scope. If more or less assertions are performed than the expected, then AssertionFailed exception raises.

assert.assertionsCountEquals(
  2,
  function() {
    assert.isNotNull(value);
    assert.isFunction(value);
  },
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

utils.wait(assert.assertionsCountEquals(
    10,
    function() {
      assert.isNotNull(value);
      utils.wait(500);
      assert.isFunction(value);
    },
    {}
  ));
Object assert.assertionsMinCount(in Number aExpectedMinCount, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assert.assertionsMinCount(), assertAssertionsMinCount()

This is the local version of assertion by minAssertions property.

This runs the function in the scope specified as the third argument or the current scope. If less assertions are performed than the expected, then AssertionFailed exception raises.

assert.assertionsMinCount(
  2,
  function() {
    myCustomAssertion(value); // includes 2 or more assertions
  },
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

utils.wait(assert.assertionsCountEquals(
    10,
    function() {
      utils.wait(myCustomAssertion(value));
    },
    {}
  ));
Object assert.assertionsMaxCount(in Number aExpectedMaxCount, in Function aTestTask, in Object aScope, [in String aMessage]) alias: assert.assertionsMaxCount(), assertAssertionsMaxCount()

This is the local version of assertion by maxAssertions property.

This runs the function in the scope specified as the third argument or the current scope. If more assertions are performed than the expected, then AssertionFailed exception raises.

assert.assertionsMinCount(
  2,
  function() {
    myCustomAssertion(value); // includes 2 or less assertions
  },
  {}
);

Moreover, this assertion returns an object for waiting. You can test features which require to be asserted after delay.

utils.wait(assert.assertionsCountEquals(
    10,
    function() {
      utils.wait(myCustomAssertion(value));
    },
    {}
  ));

Assertions for regular expression

void assert.matches(in RegExp aExpected, in String aActual, [in String aMessage]) alias: assertMatch()

AssertionFailed exception raises if the string doesn't match to the regular expression.

assert.matches(/^(success|skip)$/i, module.result);
assert.matches(/https?:\/\//, module.message);
void assert.notMatches(in RegExp aUnexpected, in String aActual, [in String aMessage]) alias: assertNotMatch()

Opposite of assert.matches().

assert.notMatches(/^(failure|error)$/i, module.result);
assert.notMatches(/(ftp|nntp):/, module.message);
void assert.pattern(in String aExpected, in RegExp aActual, [in String aMessage]) alias: assertPattern()

This is for regular expression generators. If the regular expression doesn't match to the string, AssertionFailed exception raises.

var regexp = module.mailAddressRegExp;
assert.pattern('test@example.com', regexp);
assert.pattern('My Name <test@example.com>', regexp);
void assert.notPattern(in String aUnexpected, in RegExp aActual, [in String aMessage]) alias: assertNotPattern()

Opposite of assert.pattern().

var regexp = module.mailAddressRegExp;
assert.notPattern('foobar@', regexp);
assert.notPattern('My Name (test at example.com)', regexp);

More...

You can specify a custom message for any assertion. The message will be shown in the report if the assertion is failed, so you can output details of the situation which the test fails on.

Custom assertions can be defined like as:

assert.isOK = function(aActualValue) {
  var expected = 'OK';
  assert.equals(expected, aActualValue);
}