The benefits of such tools is that you can test exactly what is displayed in the browser and simulate user actions. It’s downside is that the tests are expressed in browser related terms rather than using XForms concepts. To write these tests you need to know how XForms will be transformed into HTML and this transformation depends on the XForms implementation being used and may vary between versions.
By contrast a native XForms test environment would allow to express the tests using XForms concepts such as binds, controls and events.
The basic idea is to describe a test suite and to add actions to the form to test so that the tests are executed by the XForms engine.
Let’s say we have the following form which is a slightly adapted version of the traditional XForms “hello world”:
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms">
<title>Hello World in XForms</title>
<xf:instance id="instance" xmlns="">
<xf:bind id="greetings" nodeset="/data/Greetings"
calculate="concat('Hello ', ../PersonGivenName, '. We hope you like XForms!')"/>
<p>Type your first name in the input box. <br /> If you are running XForms, the output should be displayed in
the output area.</p>
<xf:input ref="PersonGivenName" incremental="true">
<xf:label>Please enter your first name: </xf:label>
To test that the greeting are what you’d be expecting you would enter a value in the name and check the greetings.
This test can be expressed as:
<?xml version="1.0" encoding="UTF-8"?>
<suite xmlns:xh="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
<!-- The test cases -->
<expected>Hello Eric. We hope you like XForms!</expected>
<message>The greetings should be the concatenation of "Hello ", the given name and ". We hope you like
The generation of the augmented form which will perform the tests can be done in XSLT.
To implement these tests in XForms, we can add an instance initialized with the description of the test suite and updated by XForms actions to reflect the result of the tests:
<setvalue ref="instance('instance')/PersonGivenName" id="d8e11">Eric</setvalue>
<assertEqual id="d8e15" passed="">
<actual ref="instance('instance')/Greetings" id="d8e17"></actual>
<expected id="d8e19">Hello Eric. We hope you like XForms!</expected>
<message id="d8e22">The greetings should be the concatenation of "Hello ", the given name
and ". We hope you like XForms!".</message>
Note how the transformation has added id attributes to the elements of this instance which did not already carry one. These ids will be helpful while writing the actions.
In this version I have chosen to encapsulate each action and test in its own XForms action and use custom events to trigger these actions one after the other. Also note the xf:recalculate which is necessary to make sure that the greetings are recalculated after we’ve changed the name.
Now that xfu:instance contains the result of the tests, it could be saved on a server and can also be displayed using XForms controls such as:
Note that this delay causes additional client/server exchanges for server based implementations such as Orbeon Forms and the workaround should be used specifically with XSLTForms.
What you test and what you see
In our test we check that an instance value is what we expect, but can we be sure that this instance value is displayed? This is pretty obvious in our example, but what if there was a typo in the control:
More generally speaking there is a lack of standard introspection functions, not only for controls but also on instances and testing if an instance node is relevant, readonly, required or valid is not possible without using extension functions. Some (but not all) these needs are covered by EXForms functions but their support by actual implementations has to be checked.
Despite some limitations there seems to be a nice potential in a native XForms test framework and of course your feedback is most welcome.
Ever modified an XML schema? Ever broken something while fixing a bug or adding a new feature? As withany piece of engineering, the more complex a schema is, the harder it is to maintain. In other domains, unit tests dramatically reduce the number of regressions and thus provide a kind of safety net for maintainers. Can we learn from these techniques and adapt them to XML schema languages? In this workshop session, we develop a schema using unit test techniques, to illustrate their benefits in this domain.
The workshop is run as an exchange between a customer (played by Tommie Usdin) and a schema expert (played by Eric van der Vlist).
The customer, needs a schema for her to list XML application, is puzzled by the “test first programming” technique imposed by the schema expert.
At the end of the day (or workshop), will she be converted to this well known agile or extreme programming technique adapted to the development of XML schemas?
Step 1: Getting started
Hi Eric, can you help me to write a schema?
Hi Tommie, yes, sure, what will the schema be about?
I need a vocabulary for my todo lists, with todo ite…
OK, you’ve told me enough, let’s get started
—Expert (interrupting his customer)
Get started? but I haven’t told you anything about it!
Right, but it’s never too soon to write tests when you do test first programming!
Test first programming (also called test driven development) developers create test case (usually unit test cases) before implementing a function. The test suite is run, code is written based on the result of these tests and the test suite and code are updated untill all the tests pass.
The vocabulary used to define these test cases has been inspired by the SUT (XML Schema Unit Test) project. It’s a simple vocabulary (composed of only three different element) allowing to pack several XML instances together with the outcome validation result. It uses conventions that you’ll discover during the course of this workshop.
This is the first example with non top level tf:case elements. To understand how this works, we must look in more detail to the algorithm used by the framework to split a test suite into instances. The algorithm consists in two steps:
Loop over each tf:case element
Suppression of the tf:caseelements and of the top level elements which arenot ancestors of the current tf:case element.
This description may look complex, but the result is a rather intuitive way to define sub-trees that are common to several test cases.
Now that we’ve updated the test suite, we run it again.
copy the orbeon-resources/ directory under /WEB-INF/resources/apps/in yourorbeon webapp directory
or, alternatively, copy the tefisc/ directory wherever you want, edit web.xml.savto replace<param-value>/home/vdv/projects/tefisc/orbeon-resources</param-value>by the location of this directory on your filesystem, replace /WEB-INF/web.xml by this file and restart your application server.