Browser-Side Test






Browser-Side Test

Automated, IntegrationTest, JavaScript, Regression, UnitTest, TDD, Test

Browser-Side Test


Developer Story

Devi is about to embark on upgrading a blog reader and realizes she will be tinkering with the JavaScript RSS parser. The parser will need some refactoring, but fortunately there's already a JavaScript unit test for it. She proceeds to refactor the parser, ensuring the test still passes, and continues to enhance the test as she builds in further functionality.

Problem

How can you test JavaScript components?

Solution

Create automated tests of browser-side JavaScript components. This pattern is a mapping from automated testing in other languages, where frameworks like the xUnit series (http://en.wikipedia.org/wiki/XUnite.g., JUnit; see http://junit.orgare now popular. Several frameworks do the same for JavaScript. As with other languages, you can unit test an individual JavaScript component (e.g., a function) you've created, and also perform integration tests and system tests that work across several objects. And also like other languages, you can accumulate tests into a test suite object, so it's possible to run a full regression test.

The xUnit Frameworks

This pattern is heavily influenced by the "xUnit" testing frameworks (http://en.wikipedia.org/wiki/XUnit), which includes frameworks like JUnit for Java, PHPUnit for PHP, and NUnit for .Net. If you haven't used any of these before, it's worth learning the basic principles involved in xUnit-style testing, as these apply universally to all xUnit frameworks including the JavaScript ones. Kent Beck's Test-Driven Development By Example (Addison Wesley) is an excellent overview.


The test is usually some JavaScript that performs some task and includes assertion to check the state is as expected. Usually, JavaScript frameworks include a Test Runner that will run a given set of tests, then morph the DOM to show results in a table. You usually build an HTML page, with test code embedded, and perhaps some inputs to control the test; e.g., Start and Stop buttons.

So the test is automated insofar as the programmer's not manually interacting with the application and verifying results. That's good enough while developing, but the demands of continuous integration suggest we need to go beyond that. How do you allow tools like Make, Ant, or Rake start and stop a JavaScript test?

One way to coordinate a Browser-Side Test is to simulate browser behavior. Using a JavaScript engine, you can programmatically execute some JavaScript code. For example, Java programmers have the Rhino library (a standard component of Java 6). If there's some DOM manipulation involved as well, an approach like Browser Simulation, discussed in System Test can be used. A second approach is to fire up a real browser using a shell command, specifying the test page as an argument. The test framework will then run in the browser, and the page must be configured to upload results to a service somewhere, which will expose them to the test process. The test process, upon detecting new results, shuts down the browser with an interprocess communication protocol or, failing that, kills the process.

Tool Support

Scriptaculous testing framework

The Scriptaculous framework (http://script.aculo.us) includes a unit-testing framework. It works in a similar manner as JsUnit frameworks (discussed next) and the nUnit frameworks they are modelled on. A Test Case is an object containing a sequence of methods beginning with "test"; e.g., testStringMatches( ), and optional setup( ) and teardown( ). The test is passed to a testrunner object. For each test method, the runner executes setup( ), then the test method, and then teardown( ). A library of assertions are available to verify state throughout the tests. As the tests proceed, the runner updates the UI to report on the results.

As with many Scriptaculous classes, the Runner object accepts an options argument. A particularly interesting option is resultsURL, which the runner will post results to. This is a facility that makes automated testing possible, as discussed earlier in the "Solution."

Scriptaculous also contains some functions that let you simulate mouse and keyboard; see System Test.

JsUnit framework (Hieatt)

Edward Hieatt's JsUnit (http://www.edwardh.com/jsunit/) is a testing framework that supports standard xUnit-like functionality. Each test is actually an HTML page containing the test in a script, and the HTML page is passed as an argument to a test runner page. There's a demo page available (http://www.edwardh.com/jsunit/runner/testRunner.html?testpage=www.edwardh.com/jsunit/runner/tests/jsUnitTestSuite.html).

JsUnit Framework (Schaible)

Jorg Schaible's JsUnit (http://jsunit.berlios.de/index.html) works in a similar manner as the preceding frameworks.

Code Example: Using Scriptaculous Unit-Testing

The Basic Ajax Pattern Reader (http://ajaxify.com/run/reader/) uses a queue abstraction to represent the playlist. The queue itself was developed with a Scriptaculous test case (http://script.aculo.us; see Figure). The setup method establishes a new queue:

  setup: function( ) { with(this) {
    q = new Queue( );
  }},

Scriptaculous Unit Test


An initial test method verifies the queue's initial state:

  testEmptyQueue: function( ) { with(this) {
    assertEqual(0, q.size);
    var items = q.items( );
    assertEqual(0, items.length);
  }},

Subsequent methods examine the queue as it's built up:

  testSequence: function( ) { with(this) {
    q.admit("first");
    q.admit("second");
    assertEqual(2, q.size);
    assert(util.membersEqual(["second","first"], q.items( )));
    ...
  }},

All of this is embedded an HTML file (http://ajaxify.com/run/reader/queueTest.html), which, if you visit it, will run the test and report the results. Here's the structure of the HTML file:

  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Queue Test</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script src="/run/Lib/js/util.js" type="text/javascript"></script>
    <script src="prototype.js" type="text/javascript"></script>
    <script src="unittest.js" type="text/javascript"></script>
    <script src="queue.js" type="text/javascript"></script>
    <link rel="stylesheet" href="test.css" type="text/css" />
  </head>
  <body>
  ...
  <!-- Log output -->
  <div id="testlog"> </div>
  <!-- Tests follow -->
  <script type="text/javascript" language="javascript" charset="utf-8">
    new Test.Unit.Runner({
      q: null,
      setup: function( ) { with(this) {
        q = new Queue( );
      }},
      testEmptyQueue: function( ) { with(this) {
        assertEqual(0, q.size);
        var items = q.items( );
        assertEqual(0, items.length);
      }},
      ...
  });
  </script>
  </body>
  </html>

Related Patterns

Service Test

A Service Test (see the next pattern) is a good way to complement a Browser-Side Test, since it focuses on the server-side interface rather than the browser application. Testing both is necessary to be confident that all aspects of the system are working.

Logging

Logging (Chapter 18) inside a test can help diagnose problems.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows