WUnit quick start

In this complete and runnable tutorial, you'll learn how to use WUnit in a very basic Web application with forms, files to upload and a bit of AJAX. You'll also learn how to act on the server internals.

The full WUnit documentation is available here.

Download and install

You must have Java 1.5 or higher installed in your computer. If you want to run the Web application supplied in this tutorial you don't need a servlet engine, however you're not compelled to run it since the Web application to test is described enough with some snapshots in the next sections.

Download the full binary distribution that contains WUnit, XUnit, and the RefleX engine.

Unzip reflex-0.4.0-full-bin.zip in the directory of your choice, say file:///path/to/reflex/. Hereafter, commands are launched from the directory where RefleX has been unzipped.

The Web application to test : an image gallery

The Web application to test contains few Web pages described hereafter ; it consist on inviting the user to upload images on the server and display them on a gallery page. It consists on four pages :

  • the welcome page, that contains links to the other pages below
  • a form that can upload files and set them a name
  • a display page, that displays images uploaded
  • a delete page for removing images from the gallery

Server-side, the images uploaded are stored on a temporary directory on the disk with a reference in the user session. On the delete page, images are selectable with an AJAX autocompleter that send queries to the server.

Web application details

This Web application is made with Active Tags and is runnable with RefleX, like of course any non-Active Tags Web applications. If you have to test servlets you'll be able to act on server objects as well.

You can check all the files of the image gallery application in the [REFLEX_HOME]/doc/tutorial/wunit/webapp directory. The source code of the activlet contains only about 100 lines of code. You can examine it here.

Screenshots

The Welcome page :

[www]
index.html - Web Navigator

Welcome to your image gallery !

What do you want to do ?

The Upload page :

[www]
upload.html - Web Navigator

[Home] [Gallery] [Upload] [Delete]

Image upload

Enter a name for the image :

Select an image on your disk :

The Gallery page :

[www]
gallery.html - Web Navigator

[Home] [Gallery] [Upload] [Delete]

Image gallery

event10years
eventEML2007
eventXML2006
RefleXlogo

The Delete page :

[www]
delete.html - Web Navigator

[Home] [Gallery] [Upload] [Delete]

Delete an image

Select an image to delete :


  • event10years
  • eventXML2006
  • eventEML2007

Running the gallery application

This step is optional, you can skip this section.

Open a console and at the prompt, type the following commands from the RefleX home directory (note that the (line cut) (line cut) icon means that you MUST NOT insert a line break) :

 $ java -cp reflex-0.4.0.jar:xunit-0.4.0.jar winstone.Launcher (line cut)
    --httpPort=9999 --webroot=doc/tutorial/wunit/webapp/

Then run a Web browser : http://localhost:9999/ and play with the Web application. Keep the console open while using the Web browser and to stop the server, simply hit CTRL-C on the console.

You can of course change the default port (at the same time on the command line and on the URI of the browser) if it is already busy on your system.

Designing the test suite

We will run the Web application within the server emulator, so that you don't need a servlet container such as Tomcat (or like Winstone used above). That also will allow to act directly on the objects stored in the session.

We want to ensure that when filling the form and clicking to the links we do get the expected results. For this purpose we mimick the behaviour of a human behind its browser with WUnit ; once done, all the test suite can be submitted again and again without human intervention ; in a real-world Web application, hundreds of tests would be run similarly at once in order to check that the application still works after an upgrade : this is the purpose of regression tests.

In this tutorial, we are going to write our test incrementally. We intend to test individually each page and then test a complete scenario. Each test will introduce a specific WUnit feature :

  1. basics : GET a page, make a launcher, examine the test report
  2. act on sessions server side
  3. fill a form
  4. get an XML page, send Javascript events (keyboard and click)

This should cover rather well the functional of our Web application, client side and server side.

Testing the welcome page

In WUnit (as well as in Active Tags), markups either stand for operations or are litterals. We use active tags from several modules to build our test suite : some from WUnit such as <wunit:conversation>, <wunit:GET>, <wunit:submit>, or later <wunit:fill-form>, others from the XML Control Language such as <xcl:active-sheet>, <xcl:parse-html> or <xcl:param>, and others from XUnit such as <xunit:test-case>, <xunit:assert-string-equals> or <xunit:assert-node-equals>. Everything that appear in curly braces "{" and "}" (and displayed in bold in the following listings) is an XPath expression that can also contain materials supplied by a module, such as the io:file() XPath function, and the $sys:out and $wunit:frame XPath variables. You can also refer to the index of active materials.

The first test aims to check that everything works fine. The comments are explaining what we do :

[doc/tutorial/wunit/test-suite/1-welcome-page.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<!--this is the root of the script ; it serves to declare modules that are used (namespace declarations act like library import instructions)--> <xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--set the boundary of the test case--> <xunit:test-case name="report/1-gallery-welcome" label="[Web] Welcome page"> <!--start to discuss with the servlet emulator--> <wunit:conversation application="../webapp/WEB-INF/web.xml" uri="http://www.example.com/"> <!--get the welcome page hosted in our server emulator--> <wunit:GET url="http://www.example.com/"/> <!--do we have it ? we ask to the $wunit:frame predefined property what is the HTTP response code--> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <!--is it an HTML page ?--> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--parse the static welcome page from the file system...--> <xcl:parse-html source="../webapp/index.html" name="expected" elems-name="lower"/> <!--...and compare it with those returned by the server. $wunit:document is a predefined property that refers to the DOM of the HTML document of the current page--> <xunit:assert-node-equals result="{ $wunit:document }" expected="{ $expected }"/> </wunit:conversation> </xunit:test-case> </xcl:active-sheet>

Since we will have other tests, we are creating a launcher that will run the whole test suite and build an HTML report ; right now, it is only running the single test we made :

[doc/tutorial/wunit/test-suite/run-all-tests.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<!--this is a launcher--> <xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:exp="http://ns.inria.org/active-tags/exp" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html"> <!--invoke our tests--> <exp:invoke processor="1-welcome-page.xcl"/> <!--[[[add more tests later here]]]--> <!--merge all the reports in a single one--> <xunit:merge-reports name="Summary of Web-Gallery tests" source="{ io:file( 'report/' ) }"
output="{ io:file( 'gallery-err.xml' ) }"/> <!--create an HTML version of the report--> <xcl:transform source="gallery-err.xml" output="gallery-err.html"
stylesheet="res:///org/inria/ns/reflex/util/xunit/html-report.xsl"/> <!--res:///... indicates that the XSLT stylesheet is bundled in a jar file--> </xcl:active-sheet>

Now, let's run the launcher that will invoke our test and build an HTML report (remember that the (line cut) (line cut) icon means that you MUST NOT insert a line break):

 $ java -cp reflex-0.4.0.jar:xunit-0.4.0.jar (line cut)
     org.inria.ns.reflex.ReflexCLI run doc/tutorial/wunit/test-suite/run-all-tests.xcl

In the standard output you can see a short summary :

.
Test suite    : Summary of Web-Gallery tests
   Test cases : 1 (79)
   Errors     : 0 (0)
   Failures   : 0 (0)
   Files      : 1

It indicates that a single test was run, and in parenthesis indicates the number of atomic tests. It is somewhat big because we ask to check that the 2 HTML documents (those in our file system and those returned by the server) were equals, and every element, attribute, and text content were compared.

If you open the [doc/tutorial/wunit/test-suite/gallery-err.html] file in a Web navigator, you can examine the HTML report ; if we had errors, the Web report would be more detailed than the summary displayed in the standard output.

[www]
XUnit report - Web Navigator

XUnit report

Skip
Test name
TestsErrorsFailure
0
Summary of Web-Gallery tests
1
(79)
0
(0)
0
(0)
1/1[Web] Welcome page7900

Voilà !

Testing the gallery page

In this second test, we are testing the gallery page. Unfortunately, it is empty (let's check that it is empty !). So, we are going to cheat a little bit by creating directly session objects server side and checking that they are brought back to the client. XPath is used to extract relevant parts from the HTML page.

Of course, that implies to have a knowledge of the internal of the Web application that runs server-side.

[doc/tutorial/wunit/test-suite/2-gallery-page.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--set the boundary of the test case--> <xunit:test-case name="report/2-gallery-images" label="[Web] Gallery page"> <!--start to discuss with the servlet emulator--> <wunit:conversation application="../webapp/WEB-INF/web.xml" uri="http://www.example.com/"> <!--get the gallery page--> <wunit:GET url="http://www.example.com/gallery.html"/> <!--check that the gallery is empty we should have at the end of the HTML page : <P>Empty gallery.</P>--> <xunit:assert-string-equals result="{ $wunit:document//p[last()] }" expected="Empty gallery."/> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--store directly some objects in the current session server-side (objects are stored as attributes) ; the objects stored are images from the file system--> <xcl:attribute name="item1" value="{ io:file( '../img/xml-10.png' ) }"
referent="{ $wunit:session }"/> <xcl:attribute name="item2" value="{ io:file( '../img/xml2006-logo.png' ) }"
referent="{ $wunit:session }"/> <!--get again the gallery page ; this time it shouldn't be empty--> <wunit:GET url="http://www.example.com/gallery.html"/> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--we should have 2 rows--> <xunit:assert-number-equals result="{ count( $wunit:document//tr ) }" expected="2"/> <!--first row--> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[1] }" expected="item1"/> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[2]/img/@src }" expected="images/item1"/> <!--second row--> <xunit:assert-string-equals result="{ $wunit:document//tr[2]/td[1] }" expected="item2"/> <xunit:assert-string-equals result="{ $wunit:document//tr[2]/td[2]/img/@src }" expected="images/item2"/> </wunit:conversation> </xunit:test-case> </xcl:active-sheet>

To run it, just append in the launcher (tutorial/wunit/run-all-tests.xcl) the new test to the active sheets to invoke :

  <exp:invoke processor="1-welcome-page.xcl"/>
  <exp:invoke processor="2-gallery-page.xcl"/>

Testing the upload page

There is a form to fill client side with 2 fields, one is a file to upload. After the upload, we check the message in the response page, and ensure that the file reference has been stored in the object session.

The message in the response page looks like this :

xml-10.png uploaded.

[doc/tutorial/wunit/test-suite/3-upload-page.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--set the boundary of the test case--> <xunit:test-case name="report/3-gallery-upload" label="[Web] Upload page"> <!--start to discuss with the servlet emulator--> <wunit:conversation application="../webapp/WEB-INF/web.xml" uri="http://www.example.com/"> <!--get the upload page--> <wunit:GET url="http://www.example.com/upload.html"/> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--fill the HTML form and POST it to the server--> <wunit:fill-form form="{ $wunit:document//form[@name='upload'] }"> <!--the text input is a string--> <xcl:param name="imageName" value="test1"/> <!--the file to upload, just give it a file--> <xcl:param name="imageFile" value="{ io:file( '../img/xml-10.png' ) }"/> </wunit:fill-form> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//body/div[1] ) }"
expected="xml-10.png uploaded."/> <!--check that the session object was created ; it contains a reference to a file stored in the server ; we just check the name of the file--> <xunit:assert-string-equals result="{ name( value( $wunit:session/@test1 ) ) }" expected="xml-10.png"/> </wunit:conversation> </xunit:test-case> </xcl:active-sheet>

Note that :

  • The component behind <wunit:fill-form> works with subelements (<xcl:param>) that are independant of WUnit. The parameters used here are those that appear in the HTML form.
  • We are using the standard XPath function normalize-space() in order to trim the spaces before and after the relevant information to check.
  • In the last active tag, the value() function allows to get the value bound to the attribute referred (like with PSVI when a typed data is bound after augmentation). Here, the value bound is a file reference.

Like previously, append in the launcher the new test :

  <exp:invoke processor="1-welcome-page.xcl"/>
  <exp:invoke processor="2-gallery-page.xcl"/>
  <exp:invoke processor="3-upload-page.xcl"/>

Testing the delete page

There are several stuff to test in this page :

  • the request sent by the autocompleter
  • the delete function when the form is submitted
  • the AJAX behaviour

Like previously, we are bypassing the global functionning by setting some file references in the current session.

[doc/tutorial/wunit/test-suite/4-delete-page.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--start to discuss with the servlet emulator--> <wunit:conversation application="../webapp/WEB-INF/web.xml" uri="http://www.example.com/"> <!-- ********************* XML Test ********************* --> <xunit:test-case name="report/4a-gallery-delete-ajax" label="[Web] Delete page : AJAX"> <!--store directly some objects in the current session server-side--> <xcl:attribute name="item2" value="{ io:file( '../img/xml2006-logo.png' ) }"
referent="{ $wunit:session }"/> <xcl:attribute name="item1" value="{ io:file( '../img/xml-10.png' ) }"
referent="{ $wunit:session }"/> <xcl:attribute name="another" value="{ io:file( '../img/xml2006-logo.png' ) }"
referent="{ $wunit:session }"/> <!--get the autocompleter : we'll get the objects which name starts with "it" --> <wunit:GET url="http://www.example.com/images.xml?imageName=it"/> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="application/xml"/> <!--create an inline HTML fragment of the result expected ; whitespaces are important !!! the result produced by the server doesn't contain whitespaces --> <xcl:document name="expected"><ul><li>item1</li><li>item2</li></ul></xcl:document> <!--compare the fragment get with those expected--> <xunit:assert-node-equals result="{ $wunit:document }" expected="{ $expected }"/> </xunit:test-case> <!-- ********************* Delete page ********************* --> <xunit:test-case name="report/4b-gallery-delete-form" label="[Web] Delete page : form"> <!--get the delete page--> <wunit:GET url="http://www.example.com/delete.html"/> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--fill the HTML form and submit it to the server--> <wunit:fill-form form="{ $wunit:document//form[@name='delete'] }"> <!--the text input is a string--> <xcl:param name="imageName" value="item1"/> </wunit:fill-form> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//body/div[1] ) }"
expected="xml-10.png deleted."/> <!--check that the session object was deleted, and that other session objects were preserved--> <xunit:assert-false result="{ name( value( $wunit:session/@item1 ) ) }"/> <xunit:assert-string-equals result="{ name( value( $wunit:session/@item2 ) ) }" expected="xml2006-logo.png"/> <xunit:assert-string-equals result="{ name( value( $wunit:session/@another ) ) }"
expected="xml2006-logo.png"/> </xunit:test-case> <!-- ********************* Javascript (Autocompleter Test) ********************* --> <xunit:test-case name="report/4c-gallery-delete-js" label="[Web] Delete page : Javascript"> <!--restore in the session the item that we have just deleted--> <xcl:attribute name="item1" value="{ io:file( '../img/xml-10.png' ) }"
referent="{ $wunit:session }"/> <!--what we have so far : -the delete page loaded -3 items stored in the session server side we intend to send events that will involve the Javascript autocompleter--> <!--fill the HTML form WITHOUT submitting it to the server--> <wunit:fill-form form="{ $wunit:document//form[@name='delete'] }" submit="no"> <!--fill partially the input text in order to get from the server the image names that start with "it" "item1" and "item2" will be candidate--> <xcl:param name="imageName" value="it"/> </wunit:fill-form> <!--let the client having enough time to display the result sent by the server (the autocompleter accept a delay parameter to set with Javascript) we are waiting 1000 ms--> <xcl:sleep value="1000"/> <!--click on the second item of the list--> <wunit:click target="{ $wunit:document//div[@id='list']/ul/li[2] }"/> <!--submit the form as-is : as we have clicked on the 2nd <li>, the input field should have been its value set to "item2"--> <wunit:fill-form form="{ $wunit:document//form[@name='delete'] }"/> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//body/div[1] ) }"
expected="xml2006-logo.png deleted."/> <!--check that the session object was deleted, and that other session objects were preserved--> <xunit:assert-false result="{ name( value( $wunit:session/@item2 ) ) }"/> <xunit:assert-string-equals result="{ name( value( $wunit:session/@item1 ) ) }" expected="xml-10.png"/> <xunit:assert-string-equals result="{ name( value( $wunit:session/@another ) ) }"
expected="xml2006-logo.png"/> </xunit:test-case> </wunit:conversation> </xcl:active-sheet>

Once again, we append in the launcher the new test :

  <exp:invoke processor="1-welcome-page.xcl"/>
  <exp:invoke processor="2-gallery-page.xcl"/>
  <exp:invoke processor="3-upload-page.xcl"/>
  <exp:invoke processor="4-delete-page.xcl"/>

Testing the global kinematic

Here is a real scenario that a human could perform. But WUnit can process it thousand times without complaining.

[doc/tutorial/wunit/test-suite/5-scenario.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<!--this is the root of the script ; it serves to declare modules that are used (act like a library import instruction)--> <xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--scenario : -get the "upload" page -upload a file -upload a file -click on the link "gallery" -click on the link "delete" -delete an item -click on the link "gallery"--> <!--set the boundary of the test case--> <xunit:test-case name="report/5-gallery-scenario" label="[Web] Scenario"> <!--start to discuss with the servlet emulator--> <wunit:conversation application="../webapp/WEB-INF/web.xml" uri="http://www.example.com/"> <!--get the welcome page hosted in our server emulator--> <wunit:GET url="http://www.example.com/upload.html"/> <!--do we have it ? we ask to the $wunit:frame predefined property what is the HTTP response code--> <xunit:assert-number-equals result="{ value( $wunit:frame/@wunit:response-code ) }" expected="200"/> <!--is it an HTML page ?--> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--fill the HTML form and POST it to the server--> <wunit:fill-form form="{ $wunit:document//form[@name='upload'] }"> <!--the text input is a string--> <xcl:param name="imageName" value="test1"/> <!--the file to upload, just give it a file--> <xcl:param name="imageFile" value="{ io:file( '../img/xml-10.png' ) }"/> </wunit:fill-form> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//body/div[1] ) }"
expected="xml-10.png uploaded."/> <!--fill the HTML form and POST it to the server--> <wunit:fill-form form="{ $wunit:document//form[@name='upload'] }"> <xcl:param name="imageName" value="test2"/> <xcl:param name="imageFile" value="{ io:file( '../img/xml2006-logo.png' ) }"/> </wunit:fill-form> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//body/div[1] ) }"
expected="xml2006-logo.png uploaded."/> <!--click on the link "gallery"--> <wunit:click target="{ $wunit:document//a[@href='gallery.html'] }"/> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--we should have 2 rows--> <xunit:assert-number-equals result="{ count( $wunit:document//tr ) }" expected="2"/> <!--first row--> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[1] }" expected="test1"/> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[2]/img/@src }" expected="images/test1"/> <!--second row--> <xunit:assert-string-equals result="{ $wunit:document//tr[2]/td[1] }" expected="test2"/> <xunit:assert-string-equals result="{ $wunit:document//tr[2]/td[2]/img/@src }" expected="images/test2"/> <!--click on the link "delete"--> <wunit:click target="{ $wunit:document//a[@href='delete.html'] }"/> <wunit:fill-form form="{ $wunit:document//form[@name='delete'] }"> <xcl:param name="imageName" value="test1"/> </wunit:fill-form> <!--click on the link "gallery"--> <wunit:submit event="click" target="{ $wunit:document//a[@href='gallery.html'] }"/> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--we should have 1 row--> <xunit:assert-number-equals result="{ count( $wunit:document//tr ) }" expected="1"/> <!--first row--> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[1] }" expected="test2"/> <xunit:assert-string-equals result="{ $wunit:document//tr[1]/td[2]/img/@src }" expected="images/test2"/> </wunit:conversation> </xunit:test-case> </xcl:active-sheet>

As usual, the new test is appended to the suite :

  <exp:invoke processor="1-welcome-page.xcl"/>
  <exp:invoke processor="2-gallery-page.xcl"/>
  <exp:invoke processor="3-upload-page.xcl"/>
  <exp:invoke processor="4-delete-page.xcl"/>
  <exp:invoke processor="5-scenario.xcl"/>

Running the whole test suite

We now have a bunch of tests. If you run all the tests at once, you get the following results :

.......
Test suite    : Summary of Web-Gallery tests
   Test cases : 7 (135)
   Errors     : 0 (0)
   Failures   : 0 (0)
   Files      : 7

In the HTML report [doc/tutorial/wunit/test-suite/gallery-err.html] you can notice that the XSLT traces we wrote during the test to the standard output have been captured.

Repairing errors

So far, we have tested a well-designed Web application with no errors regarding the result expected. Let's introduce a (fictious) error in the Web application... We change in the Web application what is returned when the autocompleter is involved.

The HTML error report shows clearly that 3 errors were found in some tests. The details of the former (click on [Display errors] below) indicate that the server wrongly sends an <ol> element instead of the <ul> element expected by the autocompleter. The 2 latter errors are consequences : they fail because we weren't able to get the right element and delete the image in our gallery. In a normal situation, a developer would identify the assertion that cause the error, localize the piece of code to correct in the Web application source and rerun the test suite.

You can experiment yourself what happens when you inject other errors in the Web application ; you can act directly on the source by editing the file [doc/tutorial/wunit/webapp/WEB-INF/active-sheet.web] : around line 47, you can change the local name of the element like we did, or add an unexpected attribute or set the XHTML namespace for example.

[www]
XUnit report - Web Navigator

XUnit report

 
Skip
Test name
TestsErrorsFailure
0
Summary of Web-Gallery tests
7
(124)
2
(3)
0
(0)
1/7[Web] Welcome page7900
2/7[Web] Gallery page1000
3/7[Web] Upload page400
4/7[Web] Delete page : AJAX910
5/7[Web] Delete page : form600
6/7[Web] Delete page : Javascript420
7/7[Web] Scenario1200

Testing without server emulator

Our test suite works pretty well, but what would it be if our Web application wasn't made with servlets ? We would be able to test it anyway but with less control on server-side components like we did. Any test where session objects were authoritatively stored can't be run. The tests that remain are the welcome page and the scenario.

In the code of those tests, the differences are that the @application attribute of the <wunit:conversation> elements have been removed and that the URLs submitted have to point to a real Web server where the application to test has been deployed. The following source code is almost the same as the previous one :

[doc/tutorial/wunit/test-suite-with-server/5-scenario.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<!--this is the root of the script ; it serves to declare modules that are used (act like a library import instruction)--> <xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:io="http://ns.inria.org/active-tags/io" xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xunit="http://reflex.gforge.inria.fr/xunit.html" xmlns:wunit="http://reflex.gforge.inria.fr/wunit.html"> <!--scenario : -get the "upload" page -upload a file -upload a file -click on the link "gallery" -click on the link "delete" -delete an item -click on the link "gallery"--> <!--set the boundary of the test case--> <xunit:test-case name="report/5-gallery-scenario" label="[Web] Scenario"> <!--start to discuss with the server--> <wunit:conversation> <!--get the upload page ; the server have to be started before--> <wunit:submit method="GET" url="http://localhost:9999/upload.html"/> <!--do we have it ? we ask to the $wunit:frame predefined property what is the HTTP response code--> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:response-code ) }" expected="200"/> <!--is it an HTML page ?--> <xunit:assert-string-equals result="{ string( $wunit:frame/@wunit:mime-type ) }" expected="text/html"/> <!--fill the HTML form and POST it to the server--> <wunit:fill-form form="{ $wunit:document//FORM[@name='upload'] }"> <!--the text input is a string--> <xcl:param name="imageName" value="test1"/> <!--the file to upload, just give it a file--> <xcl:param name="imageFile" value="{ io:file( '../img/xml-10.png' ) }"/> </wunit:fill-form> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//BODY/DIV[1] ) }"
expected="xml-10.png uploaded."/> <!--fill the HTML form and POST it to the server--> <wunit:fill-form form="{ $wunit:document//FORM[@name='upload'] }"> <xcl:param name="imageName" value="test2"/> <xcl:param name="imageFile" value="{ io:file( '../img/xml2006-logo.png' ) }"/> </wunit:fill-form> <!--check the message in the response page sent by the server--> <xunit:assert-string-equals result="{ normalize-space( $wunit:document//BODY/DIV[1] ) }"
expected="xml2006-logo.png uploaded."/> <!--click on the link "gallery"--> <wunit:submit event="click" target="{ $wunit:document//A[@href='gallery.html'] }"/> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--we should have 2 rows--> <xunit:assert-number-equals result="{ count( $wunit:document//TR ) }" expected="2"/> <!--first row--> <xunit:assert-string-equals result="{ $wunit:document//TR[1]/TD[1] }" expected="test1"/> <xunit:assert-string-equals result="{ $wunit:document//TR[1]/TD[2]/IMG/@src }" expected="images/test1"/> <!--second row--> <xunit:assert-string-equals result="{ $wunit:document//TR[2]/TD[1] }" expected="test2"/> <xunit:assert-string-equals result="{ $wunit:document//TR[2]/TD[2]/IMG/@src }" expected="images/test2"/> <!--click on the link "delete"--> <wunit:submit event="click" target="{ $wunit:document//A[@href='delete.html'] }"/> <wunit:fill-form form="{ $wunit:document//FORM[@name='delete'] }"> <xcl:param name="imageName" value="test1"/> </wunit:fill-form> <!--click on the link "gallery"--> <wunit:submit event="click" target="{ $wunit:document//A[@href='gallery.html'] }"/> <!--write an HTML trace to sysout--> <xcl:transform source="{ $wunit:document }" output="{ $sys:out }"/> <!--we should have 1 row--> <xunit:assert-number-equals result="{ count( $wunit:document//TR ) }" expected="1"/> <!--first row--> <xunit:assert-string-equals result="{ $wunit:document//TR[1]/TD[1] }" expected="test2"/> <xunit:assert-string-equals result="{ $wunit:document//TR[1]/TD[2]/IMG/@src }" expected="images/test2"/> </wunit:conversation> </xunit:test-case> </xcl:active-sheet>

You can launch them in the same way but this time you have to run the Web server :

 $ java -cp reflex-0.4.0.jar:xunit-0.4.0.jar winstone.Launcher (line cut)
    --httpPort=9999 --webroot=doc/tutorial/wunit/webapp/

And in a separate console :

 $ java -cp reflex-0.4.0.jar:xunit-0.4.0.jar (line cut)
     org.inria.ns.reflex.ReflexCLI run doc/tutorial/wunit/test-suite-with-server/run-tests.xcl

And now ?

Want more ?

Check the WUnit full documentation for the complete reference of the WUnit module and other tips, and the XUnit cookbook.