- Basic rules of testcases
- Simple testcases (unit-tests)
- Complex testcases (functional-tests) with deferred operations
- How to assert the number of processed assertions?
- How to run testcases in clean or ready-made profile?
- How to write data-driven tests?
- Skipping of tests by conditions
- Mapping for URIs
- UxU Home
- Testing Greasemonkey scripts by UxU
- Helper methods for testcases
- How to write testcases with mocks?
- How to control UxU from remote?
- Command line options
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 startUp()
{
// This function is processed only once before tests.
// (ex. loading the class you want to test now, etc.)
}
function shutDown()
{
// 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.
| 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; |
startUp |
Operations to set up the whole testcase at first | function.isStartUp = true; |
shutDown |
Operations to tear down the whole testcase on the end | function.isShutDown = true; |
test... |
Each test | function.isTest = true; |
They are processed in the order like this:
- codes written on the global namespace
- startUp (if exists)
- setUp (if exists)
- the first test
- tearDown (if exists)
- setUp (if exists)
- the second test
- tearDown (if exists)
- ...snip...
- setUp (if exists)
- the Nth (final) test
- tearDown (if exists)
- shutDown (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.
*On UxU 0.7.x and older versions, startUp/shutDown was called warmUp/coolDown. For backward compatibility, UxU 0.8.0 and later recognize warmUp/coolDown just same as startUp/shutDown.
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, startUp, or shutDown, 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.
| Type | Descriptions and examples |
|---|---|
| Numeric value | Specified value is recognized as a wait time in milliseconds.
|
Object which has the value property |
UxU watches the value of the object's
|
| Function(*) | UxU watches the returned value of the function. While the function returns
|
| Generator-iterator(*) | UxU does iteration for the generator-iterator again and again, until
|
| Generator(*) | UxU creates generator-iterator from the generator automatically, and does it just like as cases for generator-iterators.
|
| Deferred object of JSDeferred | UxU waits until the given Deferred chain is completely finished. If the chain was already finished, then UxU immediately resumes. Timeout: after 30 seconds.
Only instances created from the "Deferred" class defined in UxU's namespace are available for this purpose. If the tested feature uses another "Deferred" class in its namespace, Deferred objects created from the class won't work as you expected. So, you will have to inject UxU's "Deferred" class into the tested implementation. |
| Pairs of DOM event type and DOM event target | UxU waits until the specified event is fired on the paired target. If you specify multiple pairs, UxU will resume when the fastest one of them events is fired. Then you can get the fired event object as the
You can specify events with details, by using hashes.
|
| Others | 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:
| Priority | Probability | Numeric 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
*On UxU 0.8.x, you can use a local HTTP server as mock. It possibly more useful than this simple mapping feature.
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.
| Style | Descriptions 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).
|
| 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.
|
| 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,
|
*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.
Additionally, mock features of local HTTP servers will help you to write complex test about applications working with web services.