mozilla

Revision 49088 of Tutorial: Introduction to Mozmill

  • Revision slug: Mozmill/First_Steps/Tutorial:_Introduction_to_Mozmill
  • Revision title: Tutorial: Introduction to Mozmill
  • Revision id: 49088
  • Created:
  • Creator: Ctalbert
  • Is current revision? No
  • Comment 256 words added, 684 words removed

Revision Content

Let's write a simple Mozmill test to explain how each part of the Mozmill tool and API works.  If you haven't already installed the Mozmill extension, do that now.  This tutorial will cover using Mozmill in Firefox, and we will automate some shopping on the net.  We'll head to Wikipedia and search for a book.

Getting Started

Once it's installed, open Mozmill from the Tools menu to bring up the User Interface (UI) of Mozmill.  You'll notice that it comes up with some sample code immediately to get you started:

var setupModule = function (module) {
   module.controller = mozmill.getBrowserController();
};

var testFoo = function () {
   controller.open('http://www.google.com');
};

The setupModule function is used to obtain the controller instance we will use to run the test. The Mozmill controller is the core interface used to simulate user actions. The mozmill.getBrowserController call returns a controller for the currently open browser window. The testFoo function is the actual test. At the moment, all our test does is open the Google homepage, which is not very interesting.

Since we want to run more than one test in our file, we will use Mozmill's test framework features. For example, each test will open up Wikipedia's website and search for a book. So, each test will open Wikipedia's site in the beginning and each test will need to close that site at the end of the test. We can do that by adding a setupTest and teardownTest to the file. Whatever is placed in setupTest will be executed before the each test is run and whatever code is in teardownTest will be executed after each test is run. Here's what our code looks like so far:
If you run the test above in MozMill you'll notice that it runs entirely too fast to be useful. That's because all the test does right now is type in the urlbar and hit enter for each page; it doesn't
actually wait for the page to be loaded. Let's edit our test functions and add some calls that wait for the page to be loaded.

var testGoogle = function(){
  controller.open('http://www.google.com');
  controller.waitForPageLoad(controller.tabs.activeTab);
};
var testYahoo = function(){
  controller.open('http://www.yahoo.com');
  controller.waitForPageLoad(controller.tabs.activeTab);
};


Since waitForPageLoad can be used on any tab, it needs to be passed to the document that you want to wait to be loaded. Browser controllers have a convenient API for accessing tab documents and activeTab will always be the currently focused tab.

If you run the test again you'll notice a much longer time before closing each tab. Although waitForPageLoad can be very useful, in most cases it's better just to wait for the element we need.  We don't need the image to be finished loading before we can type in the search box and complete a search, so let's just wait for the search box. Let's pull up the Google test.

var testGoogle = function(){
  controller.open('http://www.google.com');
  var e = new elementslib.Name(controller.tabs.activeTab, "q");
  controller.waitForElement(e);
  controller.type(e, "mozilla");
  controller.click(new elementslib.Name(controller.tabs.activeTab, "btnG"));
  controller.sleep(3000);
};

This introduces you to elementslib. elementslib is a resource module that contains a series of Elem objects which are abstractions for interface nodes. These objects implement different methods of
describing any document interface node. Content tabs mostly contain DOM nodes and XUL(i.e. chrome) interfaces contain a mix of normal DOM nodes and special anonymous nodes; so different resolution methods are necessary for different interface nodes. Any interface node can be resolved using one of these Elem objects but since many expressions would be impossible to write by hand the MozMill IDE includes an Interface Inspector.

Load the Google search page into the browser and make sure that the Mozmill IDE window is also visible next to it. Click on the "Inspector" link in the Mozmill IDE to enable the Mozmill inspector. Now, as you mouse over elements on the browser's window, you'll notice a textbox being populated in the Mozmill Inspector dialog.  If you browse over the "Google" Image, you'll see this in the inspector dialog:

Mozmill Inspector Window

Element:: new elementslib.XPath(controller.tabs.activeTab, "/html/body/center/img")
Controller:: new mozmill.getBrowserController()
Validation:: true


The first line, "Element:: new elementslib.XPath(controller.tabs.activeTab, "/html/body/center")", is the code needed to create a new Elem object for the node you are inspecting. This is what you pass to controller.click and other user simulation methods that need elementlib objects. You'll notice that this call assumes you have a controller instance available named "controller" for the window you are inspecting. The second line, "Controller:: new mozmill.getBrowserController()", shows you what call you need to make to get that controller if you haven't created one already. The third line, "Validation:: true", is for validation of the expression. If true, it means that mozmill verified that the generated expression shown above worked and found the same object that you are currently inspecting.

Mozmill Controller Object

The Mozmill Controller object is the heart of Mozmill. It is the object responsible for controlling the application that is being tested and generating events against that application. This document explains the various things you can do with a controller object.

For more details on the Controller object, you should visit the Mozmill Controller Object

Causing Your Own Failures


The main difference between automated functional tests and automated unittests is that unittests are not all synchronous. If a call in a unit test fails, that does not necessarily mean the next call should fail.  This is why unittest APIs don't usually cause exceptions and instead tend to trigger silent framework failures.

Functional tests tend to by highly synchronous. Since tests tend to simulate user interactions any failure along the way is almost assured to cause the rest of the steps to fail. Failures in MozMill are triggered by normal JavaScript exceptions. If a failure happens inside the API, an exception is thrown. This means that if you'd like to assert something in your code you may also simply call your own exception:

var testSomething () {
  controller.open('http://www.google.com');
  controller.waitForPageLoad();
  if (controller.window.document.title != 'Yahoo') {
    throw 'Title does not match "Yahoo" !=  ' + controller.window.document.title;
  }
};

Revision Source

<p>Let's write a simple Mozmill test to explain how each part of the Mozmill tool and API works.  If you haven't already installed the <a class=" link-https" href="https://addons.mozilla.org/en-US/firefox/addon/9018" title="https://addons.mozilla.org/en-US/firefox/addon/9018">Mozmill extension</a>, do that now.  This tutorial will cover using Mozmill in Firefox, and we will automate some shopping on the net.  We'll head to Wikipedia and search for a book.</p>
<h2>Getting Started</h2>
<p>Once it's installed, open Mozmill from the Tools menu to bring up the User Interface (UI) of Mozmill.  You'll notice that it comes up with some sample code immediately to get you started:</p>
<pre class="brush: js">var setupModule = function (module) {
   module.controller = mozmill.getBrowserController();
};

var testFoo = function () {
   controller.open('<a class="moz-txt-link-freetext" href="http://www.google.com/">http://www.google.com</a>');
};

</pre>
<p>The <code>setupModule</code> function is used to obtain the controller instance we will use to run the test. The Mozmill controller is the core interface used to simulate user actions. The <code>mozmill.getBrowserController</code> call returns a controller for the currently open browser window. The <code>testFoo</code> function is the actual test. At the moment, all our test does is open the Google homepage, which is not very interesting.</p> <p>Since we want to run more than one test in our file, we will use Mozmill's test framework features. For example, each test will open up Wikipedia's website and search for a book. So, each test will open Wikipedia's site in the beginning and each test will need to close that site at the end of the test. We can do that by adding a <code>setupTest</code> and <code>teardownTest</code> to the file. Whatever is placed in <code>setupTest</code> will be executed before the each test is run and whatever code is in <code>teardownTest</code> will be executed after each test is run. Here's what our code looks like so far: <br>
If you run the test above in MozMill you'll notice that it runs entirely too fast to be useful. That's because all the test does right now is type in the urlbar and hit enter for each page; it doesn't <br>
actually wait for the page to be loaded. Let's edit our test functions and add some calls that wait for the page to be loaded.</p>
<pre class="brush: js">var testGoogle = function(){
  controller.open('<a class="moz-txt-link-freetext" href="http://www.google.com/">http://www.google.com</a>');
  controller.waitForPageLoad(controller.tabs.activeTab);
};</pre>
<pre class="brush: js">var testYahoo = function(){
  controller.open('<a class="moz-txt-link-freetext" href="http://www.yahoo.com/">http://www.yahoo.com</a>');
  controller.waitForPageLoad(controller.tabs.activeTab);
};</pre>
<p><br>
Since waitForPageLoad can be used on any tab, it needs to be passed to the document that you want to wait to be loaded. Browser controllers have a convenient API for accessing tab documents and activeTab will always be the currently focused tab.<br>
<br>
If you run the test again you'll notice a much longer time before closing each tab. Although waitForPageLoad can be very useful, in most cases it's better just to wait for the element we need.  We don't need the image to be finished loading before we can type in the search box and complete a search, so let's just wait for the search box. Let's pull up the Google test.</p>
<pre class="brush: js">var testGoogle = function(){
  controller.open('<a class="moz-txt-link-freetext" href="http://www.google.com/">http://www.google.com</a>');
  var e = new elementslib.Name(controller.tabs.activeTab, "q");
  controller.waitForElement(e);
  controller.type(e, "mozilla");
  controller.click(new elementslib.Name(controller.tabs.activeTab, "btnG"));
  controller.sleep(3000);
};</pre>
<p>This introduces you to elementslib. elementslib is a resource module that contains a series of Elem objects which are abstractions for interface nodes. These objects implement different methods of <br>
describing any document interface node. Content tabs mostly contain DOM nodes and XUL(i.e. chrome) interfaces contain a mix of normal DOM nodes and special anonymous nodes; so different resolution methods are necessary for different interface nodes. Any interface node can be resolved using one of these Elem objects but since many expressions would be impossible to write by hand the MozMill IDE includes an Interface Inspector.<br>
<br>
Load the Google search page into the browser and make sure that the Mozmill IDE window is also visible next to it. Click on the "Inspector" link in the Mozmill IDE to enable the Mozmill inspector. Now, as you mouse over elements on the browser's window, you'll notice a textbox being populated in the Mozmill Inspector dialog.  If you browse over the "Google" Image, you'll see this in the inspector dialog:<br>
<br>
<img alt="Mozmill Inspector Window" src="http://people.mozilla.org/~clint/mozmill-inspector.png"></p>
<pre>Element:: new elementslib.XPath(controller.tabs.activeTab, "<em class="moz-txt-slash"><span class="moz-txt-tag">/</span>html/body<span class="moz-txt-tag">/</span></em>center/img")</pre>
<pre>Controller:: new mozmill.getBrowserController()</pre>
<pre>Validation:: true</pre>
<p style="text-align: left;"><br>
The first line, "Element:: new elementslib.XPath(controller.tabs.activeTab, "/html/body/center")", is the code needed to create a new Elem object for the node you are inspecting. This is what you pass to controller.click and other user simulation methods that need elementlib objects. You'll notice that this call assumes you have a controller instance available named "controller" for the window you are inspecting. The second line, "Controller:: new mozmill.getBrowserController()", shows you what call you need to make to get that controller if you haven't created one already. The third line, "Validation:: true", is for validation of the expression. If true, it means that mozmill verified that the generated expression shown above worked and found the same object that you are currently inspecting.</p>
<h2>Mozmill Controller Object</h2>
<p>The Mozmill Controller object is the heart of Mozmill. It is the object responsible for controlling the application that is being tested and generating events against that application. This document explains the various things you can do with a controller object.</p>
<p>For more details on the Controller object, you should visit the <a href="/en/Mozmill/Mozmill_Controller_Object" title="en/Mozmill/Mozmill Controller Object">Mozmill Controller Object</a></p>
<h2>Causing Your Own Failures</h2>
<p style="text-align: left;"><br>
The main difference between automated functional tests and automated unittests is that unittests are not all synchronous. If a call in a unit test fails, that does not necessarily mean the next call should fail.  This is why unittest APIs don't usually cause exceptions and instead tend to trigger silent framework failures.<br>
<br>
Functional tests tend to by highly synchronous. Since tests tend to simulate user interactions any failure along the way is almost assured to cause the rest of the steps to fail. Failures in MozMill are triggered by normal JavaScript exceptions. If a failure happens inside the API, an exception is thrown. This means that if you'd like to assert something in your code you may also simply call your own exception:</p>
<pre class="brush: js">var testSomething () {
  controller.open('<a class="moz-txt-link-freetext" href="http://www.google.com/">http://www.google.com</a>');
  controller.waitForPageLoad();
  if (controller.window.document.title != 'Yahoo') {
    throw 'Title does not match "Yahoo" !=  ' + controller.window.document.title;
  }
};</pre>
Revert to this revision