One of the goals of an agile web software development team is to deliver quality software in a timely fashion. For most web applications the server-side software uses a database and the final products are HTML and/or XML. HTML is rendered by a browser and XML is either processed by a browser (AJAX) or exchanged with a partner web application (business-to-business integration). Fitnesse [12] tests can communicate with the database and can make assertions against HTML and XML. Agile web teams can use Fitnesse to create comprehensive test suites that make system-level assertions. Once these test suites exist, the team can release updates to their production web site as often as every day, confident that all of the system-level requirements (both old and new) are being met.
The purpose of this article is to illustrate the use of Fitnesse by agile web development teams. It first will discuss some of the reasons that agile web development teams would want to adopt a rapid release schedule. Next it will suggest that Fitnesse is a viable testing tool for use with this schedule. Then it will provide a number of examples to demonstrate the simplicity and power of Fitnesse. Finally, it will provide tips regarding Fitnesse test writing.
In this article, the agile term "whole team" refers to the collective group of software developers, DBAs, technical testers, managers, and customers. Agile software development is a discipline. For more information regarding agile software methodologies, see Ron Jeffries's XP web site [13].
The rapid release schedule accommodates a wide range of sizes and types of projects, because each day the whole team has the opportunity to release software updates, regardless of how long the updates took to develop. Although the whole team could probably get by without releasing its software quite as often, once a rapid release schedule is adopted, the team members will wonder how they ever managed without it.
Simple web sites can be managed effectively by a content management system and require little developer, DBA, and technical tester work. However, more complex web sites require software customizations to provide tracking services for their e-products. This tracking can include page impressions, clicked links, dynamic advertising, and links to partner sites, which can then be customized for each e-product. These types of requirements are dynamic enough to keep patterns in the software design from forming, thereby forcing the whole team to release new products more often.
Agile web developers write Java software to test their Java objects, web pages, databases, and XML documents. JUnit [14] is a popular testing framework for regression testing Java technologies. Fitnesse is analogous to JUnit in that it is a testing engine built using Java technologies. However, Fitnesse is different because its user interface is a web application with test suites created and managed using wiki markup. The key difference is that JUnit is primarily used by developers, whereas Fitnesse's user interface is friendly to non-developers, too. The developer extends Fitnesse's testing engine to expose new assertion methods. Then any team member (technical or not) can use the wiki markup to populate and run these new assertion methods. In the end, the developers use both JUnit and Fitnesse; each for different purposes.
Fitnesse describes itself as a "fully integrated standalone wiki, and acceptance testing framework" that runs test assertions using wiki markup. The wiki markup is used to create test tables whose assertions are run inside of the Java web FitServer. Upon completion of the test table, the FitServer displays a green-colored cell for each correct assertion and a red-colored cell for each failed assertion.
A fixture [15] is a Java class that parses a wiki test table and then connects the test table to methods in the Java class. Fixtures support input parameters and connect a method's output to the wiki's display. For teams that want to quickly get started, there are HTML and database fixtures [16] that can be installed and used without customization. For teams that need more functionality, the complexity of the assertions is limited only by the whole team's time and imagination, because Fitnesse's Java test fixtures are meant to be extended and customized. For instance, the developer team can write Java software that queries a database and then uses that data to assert that a web page is rendering that data correctly in HTML or XML.
HtmlFixture Asserting Text and Links Are CorrectProject requirements:
![]()
Figure 1. Mock-up of horizontal navigation including the "Daily
News" link
The HtmlFixture test table below loads a web page,
asserts that the title and meta tags are correct, asserts that the
"Daily News" display text and URL are correct, and then clicks on
the link to prove that it works. Use Fitnesse markup variables [17]
to define the HtmlFixture's URL. This will allow you
to quickly toggle the environment (develop, test, production) being
tested. The domain markup variable is defined in the
root suite so that the test runner can control which environment
all of the HTML tests will run against.
!define domain {www.jhound.com}
Also, give each tested HTML element a unique id tag
so that the Element Focus command can be used to find
and focus on the element.
<a id="rssMenuLink" href="/rss/index.do">Daily News</a>
Here is the example wiki source code. The pipe (|)
character starts and ends cells and rows.
!|com.jbergin.HtmlFixture|
|http://${domain}/|
|Element Focus|jhoundTitle|title|
|Text|www.JHound.com JavaHound- Welcome to
JavaHound|
|Element Focus|Description|meta|
|Attribute|name|Description|
|Attribute|content|Find Java information: a hub
for information regarding Java, Extreme
Programming, Web Services, and Java
certifications.|
|Element Focus|Keywords|meta|
|Attribute|name|Keywords|
|Attribute|content|java j2ee, java junit, java
hibernate, java web services, java extreme
programming, java testing, java web applications,
java struts, java servlets, java jsp, java xml,
java certification, java certification quiz, java
object/relational mapping, javaone 2004|
|Element Focus|rssMenuLink|a|
|Attribute|href|/rss/index.do|
|Text|Daily News|
|Click|nowOnDailyNewsPage|
|Element Focus|jhoundTitle|title|
|Text|www.JHound.com JavaHound- JavaHound RSS|
|Element Focus|Description|meta|
|Attribute|name|Description|
|Attribute|content|Read daily news articles|
|Element Focus|Keywords|meta|
|Attribute|name|Keywords|
|Attribute|content|java technology news, sports
news, world news, us news, political news, java
news, extreme programming news, j2ee news|
Here is the line-by-line description:
HtmlFixture.id="jhoundTitle".id="Description".Description.id="Keywords".Keywords.id="rssMenuLink".href attribute is correct.Figure 2 shows what the HtmlFixture test table
looks like after it has finished running with all of its assertions
passing.
|
|
Figure 3 shows what the HtmlFixture test table
looks like after it has finished running, with one of its assertions
failing.
|
|
Steps needed to run this example:
HtmlFixture into Fitnesse.HtmlFixture Asserting Form Values Are Correct Before and After
a Form SubmissionProject requirements:
Figure 4. Mock-up of a quiz question before the user has made a
selection

Figure 5. Mock-up of a quiz question after the user has made a
selection
The HtmlFixture test table loads a web page,
submits its form, and then asserts that the submission result
displays the "Correct Answer" page. Here is the example wiki source
code:
!|com.jbergin.HtmlFixture|
|http://${domain}/java/quiz.do|
|Element Focus|radio_answerB|input|
|Set Value|checked|
|Element Focus|form_quiz|form|
|Submit|CorrectAnswerPage|
|Element Focus|submitButton_correct|input|
|Attribute|value|Correct, Next Question|
|Attribute|type|submit|
Here is the line-by-line description:
HtmlFixture.id="answerB".id="form_quiz".id="submitButton_correct".Correct, Next
Question.submit.Figure 6 shows what the HtmlFixture test table
looks like after it has finished running with all of its assertions
passing.
|
|
Steps needed to run this example:
HtmlFixture into Fitnesse.Project requirements:
The domain and database_level markup variables [17]
are defined so that the test runner can control which environment
the test will run against:
!define database_level {prod}
!define domain {www.jhound.com}
This example is leveraging the Symbol command in
HtmlFixture. The Symbol command is used
within a HtmlFixture test table to create symbol/value
pairs. These symbol/value pairs can be referenced later in the same
test table or, as in this example, subsequent test tables. Instead
of using a static HtmlFixture test table to create the
symbol/value pairs, the first test table is a customized ColumnFixture [25]
that dynamically preloads HtmlFixture's symbol
key/value pairs. Here is the example wiki source code for the first
test table:
!|com.jhound.fitnesse.DynamicSymbolSettingFixture|
|domain|database_level|loadDynamicData?|
|${domain}|${database_level}|symbols successfully
loaded|
Here is the line-by-line description:
DynamicSymbolSettingFixture.DynamicSymbolSettingFixture's publicly
defined properties domain and
database_level to the values defined in line 3. Calls
DynamicSymbolSettingFixture's
loadDynamicData() method.domain and database_level. Asserts that DynamicSymbolSettingFixture's
loadDynamicData() method returns symbols successfully
loaded.Here is DynamicSymbolSettingFixture's Java source
code. This fixture code processes the first test table and loads
the symbol values at runtime from a database:
package com.jhound.fitnesse;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.jbergin.HtmlFixture;
import fit.ColumnFixture;
public class DynamicSymbolSettingFixture
extends ColumnFixture {
/*
* domain and database_level need to be public
* so that the Fitnesse engine can set their
* values.
*/
public String domain;
public String database_level;
public String loadDynamicData() {
String returnMessage = null;
String questionLevel = null;
String questionText = null;
Connection c = null;
try {
c = ConnectionManager.getConnection(
this.database_level);
PreparedStatement pStmt = c
.prepareStatement(
"select question_level, question_text "
+ "from quiz_questions "
+ "where question_level = 100 ");
ResultSet rs = pStmt.executeQuery();
if (rs.next()) {
questionLevel = rs
.getString("question_level");
questionText = rs
.getString("question_text");
} else {
returnMessage =
"Error: did not find any data!";
}
rs.close();
pStmt.close();
} catch (SQLException e) {
returnMessage = "Error: " + e;
}
finally {
if (c != null) {
try {
c.close();
} catch (SQLException e1) {
// do nothing
}
}
}
/*
* Set symbol key/value pairs that will be
* accessible from within the HtmlFixture test
* table.
*/
HtmlFixture.setSymbol("url", "http://"
+ this.domain + "/java/quiz.do");
HtmlFixture.setSymbol("questionValue", "$"
+ questionLevel + " Question: ");
HtmlFixture.setSymbol("questionText",
questionText);
return (returnMessage == null)
? "symbols successfully loaded"
: returnMessage;
}
}
The second test table is a HtmlFixture. The static
URLs and static assertion values, as seen in previous examples, are
replaced by the symbols url,
questionValue, and questionText. The
symbols are used to create this HtmlFixture test table
at runtime. Here is the example wiki source code for the second
test table:
!|com.jbergin.HtmlFixture|
|symbol url|
|Element Focus|span_scoreValue|span|
|Text|symbol questionValue|
|Element Focus|span_question|span|
|Text|symbol questionText|
Here is the line-by-line description:
HtmlFixture.url symbol
and parses its HTML with an XML parser.id="span_scoreValue".questionValue symbol.id="span_question".questionText symbol.This test will remain valid because the assertions are connected
to the database. The test remains valid from day to day and in
different environments (develop, test, production) because the test
dynamically accounts for any changes. Figure 7 shows what the
DynamicSymbolSettingFixture and
HtmlFixture test tables look like after they have
finished running with all of their assertions passing.
|
|
Steps needed to run this example:
HtmlFixture into Fitnesse.ColumnFixture [25]
that preloads HtmlFixture's symbol key/value
pairs.Project requirements:
Figure 8 shows the XML document that the
XMLHTTPFixture test table is going to test.
|
|
This fixture depends upon XMLUnit 1.0 [28] and www.JHound.com Fixtures [11]. This test table is
similar to the HtmlFixture test tables in Figures 2, 3, and 6. The
difference is that it loads an XML (not HTML) document over HTTP and
then uses XPath expressions to assert that the document is both
correctly formed and that it contains the correct data. The
XMLHTTPFixture is usable without modification and
currently has three assertion commands:
Exists: assert that a XML element or attribute
exists. Use @attributeName for attributes.Not Exists: assert that a XML element does not
exist.Value: assert that a XML element or attribute has
a particular text value. Use @attributeName for attributes.Here is the example wiki source code:
!|com.jhound.fitnesse.XMLHTTPFixture|
|http://${domain}/java/quiz-xml.do|
|Exists|//www.jhound.com|
|Exists|//www.jhound.com/certification-millionaire
|
|Exists|//www.jhound.com/certification-millionaire
/questions|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/@value|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/@value|$100|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/problem|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/problem|Which of the
following correctly declares and initializes an
array of Strings?|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[1]|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[1]|String[] array =
{"hello"; "bye";"testing"};|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[2]|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[2]|String[] array =
{"hello", "bye","testing"};|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[3]|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[3]|String[3] array
= {"hello", "bye","testing"};|
|Exists|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[4]|
|Value|//www.jhound.com/certification-millionaire
/questions/question[1]/answer[4]|String[] array =
new String[3] {"hello", "bye","testing"};|
|Not Exists|//www.jhound.com/certification-
millionaire/questions/question[1]/answer[5]|
Here is the line-by-line description:
XMLHTTPFixture.java.net.URLConnection and loads the
requested URL into a org.w3c.dom.Document object.SimpleXpathEngine is used to assert
that the Document object's expected elements and
attributes exist and that they contain the correct the data.Figure 9 shows what the XMLHTTPFixture test table
looks like after it has finished running with all of its assertions
passing.
|
|
Figure 10 shows what the XMLHTTPFixture test table
looks like after it has finished running with a couple of its
assertions failing.
|
|
With a little more work to the XMLHTTPFixture, these
test tables could be made dynamic like the test tables in Figure 7 [31].
Steps needed to run this example:
XMLHTTPFixture into Fitnesse.HtmlFixture. Start with HtmlFixture
because it is easy to use, and then graduate to more dynamic
tests.This article demonstrated the power of Fitnesse as a system-level testing tool for web applications. Developing comprehensive system-level tests will provide rapid feedback to the whole team, thus preventing mistakes in integration and promotion. The tests track the correctness of the system and will eventually become as important as the web software they test. Having this test safety net allows the whole team to maintain a rapid release schedule and deliver smaller projects simultaneously with larger ones. In conclusion, Fitnesse is good for the whole team (web developers, DBAs, technical testers, managers, and customers) because everyone can get involved in Fitnesse test writing and running.
The next advancement in your Fitnesse test writing is to further
integrate the tests with the database. For example, what if a web
application needs to record the number of times a particular link
has been clicked? The answer is to write and run a custom
ColumnFixture to check the current number of clicks,
then run a HtmlFixture test table to click the link,
and then re-run the custom ColumnFixture test table
again to confirm that the number of clicks has increased by
one.
The author would like to thank Gary Brown for teaching him the agile development methodology. He would also like to thank Kelli Moran-Miller and Travis Fritts for reviewing this article and providing invaluable feedback.
Links:
[1] http://www.java.net/author/robert-j-miller
[2] http://www.java.net/article/2005/11/21/fitnesse-testing-fast-paced-agile-web-development#why-updates
[3] http://www.java.net/article/2005/11/21/fitnesse-testing-fast-paced-agile-web-development#why-fitnesse
[4] http://www.java.net/article/2005/11/21/fitnesse-testing-fast-paced-agile-web-development#text-links
[5] http://www.java.net/%3Fpage%3D2%2523values
[6] http://www.java.net/%3Fpage%3D2%2523preprocessing
[7] http://www.java.net/%3Fpage%3D3%2523xml-output
[8] http://www.java.net/%3Fpage%3D3%2523tips
[9] http://www.java.net/%3Fpage%3D3%2523conclusion
[10] http://www.java.net/%3Fpage%3D3%2523what-next
[11] http://www.java.net/%3Fpage%3D3%2523resources
[12] http://www.fitnesse.org/
[13] http://www.xprogramming.com/xpmag/whatisxp.htm
[14] http://www.junit.org/
[15] http://www.fitnesse.org/FitNesse.FixtureCode
[16] http://fitnesse.org/UsefulFixtures
[17] http://fitnesse.org/FitNesse.MarkupVariables
[18] http://www.java.net/images/2005/11/figure2.gif
[19] http://www.java.net/images/2005/11/figure3.gif
[20] http://www.fitnesse.org/FitNesse.DownloadingAndInstallingFitNesse
[21] http://fitnesse.org/FitNesse.HtmlFixture
[22] http://www.java.net/%3Ccs_comment
[23] http://www.java.net/images/2005/11/figure6.gif
[24] http://www.java.net/%3Fpage%3D2%2523Figure4
[25] http://fitnesse.org/FitNesse.FixtureCode
[26] http://www.java.net/images/2005/11/figure7.gif
[27] http://www.java.net/images/2005/11/figure8.gif
[28] http://xmlunit.sourceforge.net/
[29] http://www.java.net/images/2005/11/figure9.gif
[30] http://www.java.net/images/2005/11/figure10.gif
[31] http://www.java.net/%3Fpage%3D2%2523Figure7
[32] http://www.java.net/article/2005/11/21/fitnesse-testing-fast-paced-agile-web-development#resources
[33] http://www.xprogramming.com/xpmag/whatisxp.htm#test
[34] http://www.java.net/today/2005/11/22/jhound-fixtures.jar
[35] http://www.JHound.com
[36] http://www.jhound.com:81/JhoundWiki
[37] http://www.fitnesse.org/FitNesse.HtmlFixture
[38] http://htmlunit.sourceforge.net/
[39] http://www.carfax.com/
[40] http://business.missouri.edu/47/default.aspx
[41] http://www.missouriinnovation.com/