Writing Concordion Specifications in HTML and Markdown

Writing Concordion Specifications in HTML and Markdown

A Concordion specification is a readable document first and a test harness second. The commands embedded in the HTML are light enough that a product manager can understand the spec without knowing what they do. This post covers the full set of commands you will use day-to-day and shows how to switch to Markdown if HTML feels too heavy.

HTML Spec Structure

Every Concordion HTML file starts with the namespace declaration:

<html xmlns:concordion="http://www.concordion.org/2007/concordion">
<body>
  <!-- spec content here -->
</body>
</html>

Commands are attributes prefixed with concordion:. They can be applied to any HTML element. The element's text content provides the expected value or the input data depending on the command.

concordion:set

Assigns the element's text to a variable:

<p>
  User <span concordion:set="#username">alice</span> submits a request.
</p>

After this, #username holds the string "alice". Variables are scoped to the current example block.

concordion:assertEquals

Calls a fixture method and compares the result to the element's text:

<p>
  The welcome message is
  <span concordion:assertEquals="welcomeMessage(#username)">Welcome, alice!</span>.
</p>

If welcomeMessage("alice") returns "Welcome, alice!", the element turns green. Any difference turns it red and shows the actual value.

concordion:execute

Used when you need to call a fixture method that performs an action but does not return a value you want to assert on:

<p concordion:execute="processOrder(#orderId)">
  Processing order <span concordion:set="#orderId">ORD-001</span>.
</p>

You can also use concordion:execute to capture a return value into a variable for later use:

<span concordion:execute="#result = calculate(#input)">ignored text</span>

concordion:verifyRows

This command iterates over an HTML table and checks each row against items returned by a fixture method. It is the right tool when your spec asserts over a list of results.

Spec:

<table concordion:verifyRows="#product : productsInCategory('Electronics')">
  <tr>
    <th>Name</th>
    <th>Price</th>
  </tr>
  <tr>
    <td concordion:assertEquals="#product.name">Laptop</td>
    <td concordion:assertEquals="#product.price">999</td>
  </tr>
  <tr>
    <td concordion:assertEquals="#product.name">Phone</td>
    <td concordion:assertEquals="#product.price">499</td>
  </tr>
</table>

Fixture:

public Iterable<Product> productsInCategory(String category) {
    return productService.findByCategory(category);
}

Concordion checks each row in order. Missing rows and extra rows both cause failures. Use this when order and completeness matter.

Parameterized Examples with Tables

For running the same assertion with multiple inputs, use a table with concordion:execute on the row element:

<table>
  <tr>
    <th>Input</th>
    <th>Expected Output</th>
  </tr>
  <tr concordion:execute="#result = transform(#input)">
    <td concordion:set="#input">hello</td>
    <td concordion:assertEquals="#result">HELLO</td>
  </tr>
  <tr concordion:execute="#result = transform(#input)">
    <td concordion:set="#input">world</td>
    <td concordion:assertEquals="#result">WORLD</td>
  </tr>
</table>

Each row is an independent example. In the output report, each row shows its own pass/fail state.

Markdown Alternative

Concordion 2.x supports Markdown specs. The file extension changes to .md and you use a slightly different command syntax with inline attributes.

# Greeting Feature

When a user named [Bob](- '#name') logs in, the greeting is
[Hello, Bob!](- '?=greetingFor(#name)').

The link text is the expected value or the variable value. The link target is the Concordion command. The ?= prefix means assertEquals. A bare #name in the target means set.

For execute: [ignored](- 'methodName(#var)').

For verifyRows in Markdown, you write a table with a header row that contains the command:

| Name      | Price |
|-----------|-------|
| [Electronics](- '#category') products: | |

| [name][price] |
|---------------|
| Laptop | 999 |
| Phone  | 499 |

[productsInCategory(#category)][name]: concordion:assertEquals="#product.name"
[productsInCategory(#category)][price]: concordion:assertEquals="#product.price"

The Markdown syntax is less verbose for prose-heavy specs. For table-heavy specs, the HTML approach is easier to read and maintain.

Choosing Between HTML and Markdown

Use HTML when:

  • Your spec has complex tables and multiple commands per element
  • You want full control over styling in the output report
  • Non-developers will be editing the spec in a WYSIWYG editor

Use Markdown when:

  • The spec is mostly prose with a few inline assertions
  • Your team writes documentation in Markdown and prefers consistency
  • You want to render the spec in GitHub or a wiki without Concordion installed

Both formats produce the same annotated HTML output report after a test run.

Next Step

Once you are comfortable with the command set, look at concordion:matchStrategy for controlling how verifyRows handles ordering, and explore the Matchers class in your fixture for partial string matching in assertEquals.

Read more

Start now free