Concordion vs FitNesse: Acceptance Testing Comparison
Concordion and FitNesse both implement acceptance testing through executable specifications, but they make fundamentally different choices about how specs are written, where they live, and who maintains them. Choosing the wrong one creates friction that compounds over time.
Core Philosophy
FitNesse is a wiki. You run a standalone server, write specs in a browser-based wiki editor, and store everything in flat files on disk managed by FitNesse's own version control-like system. Tables are the primary spec structure. Every input and expected output lives in a cell.
Concordion is a library. Specs are files in your project repository, written in HTML or Markdown, checked into version control alongside the code they test. The document structure is freeform prose; commands are attributes on HTML elements rather than special table cells.
This is the decisive difference. FitNesse specs live outside your repository by default. Concordion specs live inside it.
Spec Format Comparison
A FitNesse spec for testing a discount calculation looks like this (Decision Table fixture):
!|DiscountCalculator|
|price|quantity|discount?|
|100 |1 |0 |
|100 |5 |10 |
|100 |10 |25 |The table header row maps to a fixture class. Each subsequent row is a test case. The column names map directly to fixture methods.
The same scenario in Concordion HTML:
<table>
<tr>
<th>Price</th>
<th>Quantity</th>
<th>Expected Discount</th>
</tr>
<tr concordion:execute="#discount = calculateDiscount(#price, #qty)">
<td concordion:set="#price">100</td>
<td concordion:set="#qty">1</td>
<td concordion:assertEquals="#discount">0</td>
</tr>
<tr concordion:execute="#discount = calculateDiscount(#price, #qty)">
<td concordion:set="#price">100</td>
<td concordion:set="#qty">5</td>
<td concordion:assertEquals="#discount">10</td>
</tr>
</table>FitNesse is more concise for pure tabular data. Concordion is more flexible when the spec needs narrative context around the table.
Setup Complexity
FitNesse requires running a server. The typical setup:
- Download the FitNesse JAR
- Start the server:
java -jar fitnesse-standalone.jar -p 8080 - Write specs in the browser
- Configure fixture classpath through the wiki
The server adds a moving part. CI integration requires starting the server as part of the build, capturing output, and shutting it down. Teams sometimes run FitNesse as a persistent service, which creates state management problems.
Concordion setup:
- Add the Maven or Gradle dependency
- Write an HTML file
- Write a JUnit fixture class
- Run
mvn test
No server. No separate process. Specs run as part of your standard test suite. CI integration is identical to any other JUnit test.
Java Integration
FitNesse uses reflection to map table column names to fixture methods. A column named discount? (with the question mark) calls a discount() method. A column named price (without the question mark) calls a setPrice() setter. This convention is not obvious until you have read the documentation, and it produces fixture classes that look different from normal Java:
public class DiscountCalculator {
private int price;
private int quantity;
public void setPrice(int price) { this.price = price; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public int discount() {
return DiscountService.calculate(price, quantity);
}
}Concordion fixture methods are named explicitly in the spec. You can call them whatever makes sense, and they take parameters directly:
@RunWith(ConcordionRunner.class)
public class DiscountTest {
public int calculateDiscount(int price, int quantity) {
return DiscountService.calculate(price, quantity);
}
}The Concordion fixture reads more naturally as a Java class. The method signature matches what the spec calls.
Team Collaboration
FitNesse suits teams where business analysts want to own and edit specs directly. The wiki interface is accessible without a development environment. An analyst can log into the FitNesse server, add a new row to a table, and save the spec without touching a terminal or a code editor. The downside is that the spec lives outside version control unless you set up FitNesse's git integration plugin, which adds complexity.
Concordion specs are files in a git repository. Editing them requires either a code editor or a text editor, and submitting changes requires a commit. For teams where developers own the acceptance test suite, this is fine. For teams where business analysts need write access, it introduces a barrier.
Output Reports
Both frameworks produce HTML output reports after a test run. FitNesse's reports are generated on the wiki server and accessible through the browser interface. Concordion's reports are files on disk, typically in target/concordion. Concordion reports are easy to archive and publish as CI artifacts. FitNesse reports require the server to be running to view them.
When to Use Concordion
- Your team commits specs to version control alongside code
- Specs need narrative prose mixed with examples
- You want zero infrastructure beyond Maven or Gradle
- Developers write and maintain most of the specs
When to Use FitNesse
- Business analysts or QA managers need to write and edit specs without a development environment
- Your tests are heavily tabular with many input/output rows and minimal prose
- You want a persistent, browseable spec library that non-technical stakeholders can navigate
Next Step
If you choose Concordion, the next investment is configuring the Storyboard Extension to build visual narratives of multi-step workflows. It is particularly effective for end-to-end browser tests where each step needs a screenshot attached to the corresponding spec section.