Skip to content
🎉 TableTest 1.2.0 is out — array parameter support and Java 8+. Read the changelog.
Basic Usage

Basic Usage

This guide covers the fundamental concepts of TableTest: table structure, value formats, and execution model.

Table Structure

A TableTest table is a pipe-delimited text block. Columns are separated by pipes (|), and each line is a row. Pipes are only used between columns — not at the start or end of a line.

@TableTest("""
    Column1 | Column2 | Column3
    value1  | value2  | value3
    value4  | value5  | value6
    """)

Header Row

The first row defines column names. Columns map to method parameters by order, so you can use readable header names with whitespace and punctuation:

@TableTest("""
    Input Value | Result?
    5           | 10
    """)
void headerRow(int input, int expected) {
    assertEquals(expected, input * 2);
}

Data Rows

Each data row represents one test execution. A table with 3 data rows produces 3 test executions.

@TableTest("""
    Value | Result
    1     | 2
    3     | 6
    5     | 10
    """)
void testDouble(int value, int result) {
    assertEquals(result, value * 2);
}

Scenario Column

If there is one additional column than parameters, TableTest will use the leftmost column as the scenario column. Scenario values appear as test display names in reports and are not passed to the method:

@TableTest("""
    Scenario | Input Value | Result?
    Doubles  | 5           | 10
    """)
void headerRowScenario(int input, int expected) {
    assertEquals(expected, input * 2);
}

See Scenario Names for explicit scenario columns and other options.

Execution Model

TableTests are JUnit parameterized tests under the hood. They follow the standard JUnit execution model.

One Execution Per Row

Each data row triggers one method invocation:

@TableTest("""
    Scenario | Value
    First    | 1
    Second   | 2
    Third    | 3
    """)
void oneExecutionPerRow(int value) {
    System.out.println("Value: " + value);
}

Independent Executions

Each execution is independent. State doesn’t carry over between rows:

private int counter = 0;

@TableTest("""
    Scenario | Value
    First    | 1
    Second   | 2
    """)
void independentExecutions(int value) {
    assertEquals(1, ++counter);  // counter will initialise to 0 for each row
}

Best practice: Keep tests stateless. Each row should be independently verifiable.

Test Lifecycle

For each row:

  1. TableTest reads the row values
  2. Converts values to parameter types
  3. Asks JUnit to run the test method with those parameters

JUnit lifecycle methods (@BeforeEach, @AfterEach) run for each row.

Value Formats

TableTest supports four value formats: single values, lists, sets, and maps. These can be nested to create complex data structures.

Single Values

Single values are converted to the corresponding parameter type — primitives, strings, enums, dates, and other types supported by JUnit’s built-in converters.

Values can appear with or without quotes. Surrounding single (') or double (") quotes are required when the value contains characters that could be confused with table syntax (such as |, [, or {). Whitespace around unquoted values is trimmed. To preserve leading or trailing whitespace, use quotes.

Empty values are represented by adjacent quote pairs ("" or '').

@TableTest("""
    Value                  | Length?
    Hello, world!          | 13
    "cat file.txt | wc -l" | 20
    "[]"                   | 2
    ''                     | 0
    """)
void testString(String value, int expectedLength) {
    assertEquals(expectedLength, value.length());
}

When single values appear as elements inside collections (lists, sets, or maps), collection syntax characters also require quoting.

Lists

Lists convert to List or array parameter types. Lists are enclosed in square brackets with comma-separated elements. Lists can contain single values or compound values (nested lists, sets, or maps). Empty lists are represented by [].

@TableTest("""
    List      | Size? | Sum?
    []        | 0     | 0
    [1]       | 1     | 1
    [3, 2, 1] | 3     | 6
    """)
void integerList(List<Integer> list, int expectedSize, int expectedSum) {
    assertEquals(expectedSize, list.size());
    assertEquals(expectedSum, list.stream().mapToInt(Integer::intValue).sum());
}

Sets

Sets convert to Set parameter types. When the parameter is not a Set, curly braces denote a value set instead — each element becomes a separate test invocation. Sets are enclosed in curly braces with comma-separated elements. Sets can contain single values or compound values. Empty sets are represented by {}.

@TableTest("""
    Set       | Size?
    {1, 2, 3} | 3
    {Hello}   | 1
    {}        | 0
    """)
void testSet(Set<String> set, int expectedSize) {
    assertEquals(expectedSize, set.size());
}

Maps

Maps convert to Map parameter types. Maps use square brackets with comma-separated key-value pairs, where colons separate keys and values. Keys must be unquoted single values without table or collection syntax characters. Values can be single (unquoted or quoted) or compound (list, set, or map). Empty maps are represented by [:].

@TableTest("""
    Map                        | Size?
    [one: 1, two: 2, three: 3] | 3
    [:]                        | 0
    """)
void testMap(Map<String, Integer> map, int expectedSize) {
    assertEquals(expectedSize, map.size());
}

Nested Structures

Lists, sets, and maps can be nested to create complex data structures. TableTest converts nested values recursively using generic type information from the test method parameter.

@TableTest("""
    Scores                           | Top scorer?
    [Alice: [90, 80], Bob: [70, 60]] | Alice
    [Alice: [75, 85], Bob: [95, 90]] | Bob
    """)
void testNestedParameterizedTypes(
    Map<String, List<Integer>> scores,
    String expectedTopScorer
) {
    assertEquals(expectedTopScorer, topScorer(scores));
}

Null Values

A blank cell represents the absence of a value — there is no null keyword. Simply leave the cell empty.

@TableTest("""
    String | Integer | List | Map | Set
           |         |      |     |
    """)
void blankConvertsToNull(
    String string, Integer integer, List<?> list,
    Map<String, ?> map, Set<?> set
) {
    assertNull(string);
    assertNull(integer);
    assertNull(list);
    assertNull(map);
    assertNull(set);
}

A blank cell converts to null for all object types, including String, wrapper types, and object arrays. Primitive types (int, boolean, etc.) and primitive arrays (int[], etc.) cannot represent null — use their wrapper equivalents (Integer, Boolean[]) when a parameter may be blank.

Next Steps