As a followup to making my first patch for Firefox, it came time to write some automated tests for it. Turns out Mozilla has an entire suite of automated testing tools for their platforms. All thoroughly documented, they range from the ultra low-level compiled code tests to “record and play” macro testing for human UI interactions.
Selecting the Test Suite
The hardest part was figuring out what test suite would best compliment my patch, but given that my patch affected the Browser Chrome I went with that section of the Mochitest suite. Browser Chrome tests are really just javascript files running at elevated chrome-level privileges. This means that in them you get to access the tab browser API, more window objects, and other goodies. They’re really simple to write, just follow one convention: put everything in function test(). This is the main line of your test mini-application. You can add more functions, but they’ll be ignored by the tester.
At first I’d put my js file in the source tree directory only ($srcRoot/browser/base/content/test) but turns out that builds compiled with testing enabled pull from a different directory ($objDir/_tests/testing/mochitest/browser/browser/base/content/test). Oh yeah that’s right: Firefox has to be built with the –enable-tests option. This pushes the test code from the source into the appropriate sub-directory of your output object directory (notice how both end in “/browser/base/content/test”?). Since I had written my test after compiling, I had to copy it over myself!
With the right location, it was time to write the test. Simple enough, I just looked at existing tests. The browser_allTabsPanel.js one looked very similar to what I was trying to do, so I used it to get a feel for how to write a test.
Writing the Test
When writing a test you must work by assertions, specifying a message to output when the expected value matches the actual. A few assertion functions are:
ok(val, "val exists!"); // Tests existence is(val1, val2, "val1 equals val2"); // Tests equality isnot(val1, val2, "val1 does not equal val2"); // Tests if not equal
For a successful test run, you want all of the above comparisons (if used) to be true. As for writing the code itself: I wanted to test the addTab function I’d modified in tabbrowser.xml. To do this, I used the gBrowser object, which seems to be the global object holding the current browser (and giving access to functions in tabbrowser.xml). Here was my test function:
function test() {
var stubTab = gBrowser.addTab();
var tabs = gBrowser.tabs;
var owner;
is(tabs.length, 2, "2 tabs are open");
is(gBrowser.selectedTab._tPos, 0, "First tab is selected");
var newTab = gBrowser.addTab();
is(gBrowser.selectedTab, tabs[0], "First tab is still selected");
is(gBrowser.selectedTab._tPos+1, newTab._tPos, "Was inserted at #2");
is(newTab._tPos+1, stubTab._tPos, "Old #2 shifted to #3");
gBrowser.moveTabTo(newTab,2);
is(newTab._tPos-1,stubTab._tPos, "Successfully moved new tab, older one shifted down");
gBrowser.removeTab(stubTab);
is(newTab, tabs[1],"Successfully deleted stub tab, new tab moved down");
var newTab2 = gBrowser.addTab();
is(newTab, tabs[2],"Successfully moved newTab down on newTab2 creation");
is(newTab2, tabs[1],"Successfully added newTab2 adjacent to selected tab");
gBrowser.selectTabAtIndex(1);
var newTab3 = gBrowser.addTab();
is(newTab3, tabs[2],"Successfully added newTab3 in middle of list");
while (tabs.length > 1)
gBrowser.removeCurrentTab();
}
Running the Test
With the test written, it was time to run it. For that, I had to go back to command line and from the source root run
make -C $(OBJDIR) mochitest-browser-chrome
This ran the entire suite! I didn’t want to wait around for my one test to run, so instead I specified the TEST_PATH variable before running it:
TEST_PATH=browser/base/content/test/browser_tab_addBeside.js make -C $(OBJDIR) mochitest-browser-chrome
Much better, it only runs my test. And 11/11 passed! Perfect.

Leave a Reply