Getting Started with FitNesse for Acceptance Testing in Java
FitNesse is a web-based wiki and acceptance-testing framework, originally built by Robert C. Martin as an extension of Ward Cunningham’s Fit framework. It lets business analysts, testers, and developers describe expected behavior as plain tables on a wiki page — no Java knowledge required to write the test case itself — while a thin Java class called a fixture connects that table to the real application code. The wiki page becomes the executable specification: run it, and every row turns green or red depending on whether the application produced the expected result.
1. Understanding the FitNesse Framework
A FitNesse installation is a small standalone server that hosts wiki pages. Each test page contains one or more tables, and FitNesse supports a few table styles:
- Decision Table — a spreadsheet-like table of inputs and expected outputs; one fixture instance runs per data row.
- Script Table — a sequence of fixture method calls, useful for multi-step workflows.
- Query Table — verifies that a query-like fixture method returns an expected set of rows.
When a test page runs, FitNesse hands each table to Slim — its lightweight test runner protocol — which starts a small server process, reflectively creates your fixture class, calls setter methods matching the column headers, invokes the method under test, and compares the returned value against the expected cell. The comparison result (pass, fail, or exception) is what gets rendered back onto the wiki page as color.
2. Maven Dependency
FitNesse ships as a single Maven artifact that bundles both the wiki server and the Slim fixture API you compile your fixtures against. Add it as a test-scoped dependency:
<dependency>
<groupId>org.fitnesse</groupId>
<artifactId>fitnesse</artifactId>
<version>20250223</version>
<scope>test</scope>
</dependency>
FitNesse releases are versioned by date (YYYYMMDD), so 20250223 simply means the build published on that date — check Maven Central for the latest; the test scope keeps the wiki server and Slim runner off your production classpath, and no other dependency is needed since the Slim protocol classes your fixtures extend are bundled in this same artifact.
3. Example Project
With the dependency added, the next step is to write the actual code under test. Suppose the system under test is a small Calculator class with a divide method — this is ordinary application code, written and compiled exactly as it would be without FitNesse in the picture.
public class Calculator {
public double divide(double numerator, double denominator) {
if (denominator == 0) {
throw new ArithmeticException("Cannot divide by zero");
}
return numerator / denominator;
}
}
divide is plain business logic with no awareness of FitNesse, wikis, or tests — this is intentional, since fixtures are meant to be a thin adapter layer rather than a place to put application logic — and throwing ArithmeticException on a zero denominator gives the fixture a real error condition to surface back to the wiki page later.
4. Writing an Acceptance Test
With Calculator written and compiled, the next step is to define the acceptance criteria for it — a fixture class that exposes Calculator to a wiki Decision Table — and then run that table to verify the code behaves as expected. The fixture’s setter names must match the table’s input columns, and its method name (with parentheses in the table) must match the expected output column.
public class DivisionFixture {
private double numerator;
private double denominator;
private final Calculator calculator = new Calculator();
public void setNumerator(double numerator) {
this.numerator = numerator;
}
public void setDenominator(double denominator) {
this.denominator = denominator;
}
public double quotient() {
return calculator.divide(numerator, denominator);
}
}
Slim finds the numerator and denominator columns in the wiki table and calls setNumerator(...)/setDenominator(...) for each data row before evaluating it; quotient() is a query method — because the table column is written with parentheses, Slim calls it and compares the return value against the cell’s expected text — and the fixture delegates to calculator.divide(...) rather than reimplementing division, keeping it a pure adapter between the wiki table and the real class.
To actually run this table, start the standalone FitNesse server, pointing it at the classpath where Calculator and DivisionFixture are compiled, then open the wiki page in a browser:
java -cp fitnesse-standalone.jar:target/classes fitnesseMain.FitNesseMain -p 8080
This starts FitNesse’s embedded web server on port 8080 with your compiled fixture classes on the classpath, so Slim can load DivisionFixture by name when the page runs. Opening http://localhost:8080/DivisionTest in a browser shows the wiki page with its Decision Table, and clicking the page’s Test button (or hitting http://localhost:8080/DivisionTest?test directly) tells FitNesse to execute every row — Slim drives DivisionFixture for each one and FitNesse re-renders the same page with pass/fail/exception coloring applied.
Running this page produces a color-coded result; the plain-text rendering of that output looks like this:
Output (FitNesse test page result):
Division Fixture
numerator | denominator | quotient()
10 | 2 | 5.0 [pass]
9 | 3 | 3.0 [pass]
7 | 0 | error: Cannot divide by zero [exception]
Assertions: 2 right, 0 wrong, 0 ignored, 1 exception
The first two rows render with a green background because the returned quotient() value matched the expected cell exactly. The third row renders yellow and shows the exception message inline, since calculator.divide(7, 0) threw instead of returning a value — this is FitNesse surfacing a real defect (or confirming expected error-handling) directly on the business-readable page, with no stack trace hunting required.
5. Conclusion
FitNesse separates “what should happen” from “how it’s implemented”: the wiki table is the specification anyone on the team can read and edit, while the fixture is a small, disposable Java class that wires that specification to real code. Once a fixture like DivisionFixture exists, adding new test cases is just adding new rows to a table — no recompilation, no new code — which is what makes FitNesse effective for acceptance tests that business stakeholders need to understand and maintain alongside developers.

