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

Advanced Features

TableTest provides features beyond the basics for expressing comprehensive examples: scenario names for labelling test cases, value sets for testing multiple inputs, comments for organising tables, external files for large datasets, JUnit-supplied parameters, and escape sequences for special characters.

Scenario Names

TableTest supports providing a description for each row through a scenario column. Scenario names help explain what is being tested and appear in test reports.

Implicit Scenario Column

When the table contains one additional column than parameters, the leftmost column implicitly becomes the scenario column. The header name for this column can be anything you like.

@TableTest("""
    Situation    | Input | Expected
    Small number | 5     | 10
    Large number | 100   | 200
    Zero         | 0     | 0
    """)
void implicitScenario(int input, int expected) {
    assertEquals(expected, input * 2);
}

Scenario names appear in test reports, making it easy to identify problematic cases:

✓ ExampleTest
  ✓ testDouble (int, int)
    ✓ [1] Small number
    ✓ [2] Large number
    ✗ [3] Zero

Explicit Scenario Column

If you want another column to be the scenario column, you need to provide a parameter for all columns and annotate it with @Scenario:

@TableTest(value = """
    Input | Description  | Expected
    5     | Small number | 10
    100   | Large number | 200
    """)
void explicitScenario(int input, @Scenario String description, int expected) {
    assertEquals(expected, input * 2);
}

When using JUnit-supplied parameters like TestInfo or @TempDir, the scenario column requires an explicit @Scenario annotation — see JUnit-Supplied Parameters.

No Scenario Column

When there is no implicit or explicit scenario column, TableTest will use the column values as a test description.

@TableTest("""
    Input | Expected
    5     | 10
    100   | 200
    """)
void noScenario(int input, int expected) {
    assertEquals(expected, input * 2);
}

When running this test:

✓ ExampleTest
  ✓ testDouble (int, int)
    ✓ [1] 5, 10
    ✓ [2] 100, 200

Value Sets

Value sets let you test multiple inputs with the same expected outcome, expressed compactly within a single row. When a cell contains a set (enclosed in curly braces) and the corresponding parameter is not declared as a Set type, TableTest expands it into separate test invocations — one per element.

@TableTest("""
    Scenario                              | Example years      | Is leap year?
    Years not divisible by 4              | {2001, 2002, 2003} | false
    Years divisible by 4                  | {2004, 2008, 2012} | true
    Years divisible by 100 but not by 400 | {2100, 2200, 2300} | false
    Years divisible by 400                | {2000, 2400, 2800} | true
    """)
public void testLeapYear(Year year, boolean expectedResult) {
    assertEquals(expectedResult, year.isLeap(), "Year " + year);
}

This table produces 12 test executions — three per row, one for each value in the set. With scenario names, the display name includes both the scenario and the actual value used, making it easy to pinpoint failures.

Cartesian Product

When multiple columns contain value sets, TableTest generates the Cartesian product — all combinations of values:

@TableTest("""
    Scenario       | x         | y       | even sum?
    Even plus even | {2, 4, 6} | {8, 10} | true
    Odd plus even  | {1, 3, 5} | {6, 8}  | false
    """)
void testEvenOddSums(int x, int y, boolean expectedResult) {
    boolean isEvenSum = (x + y) % 2 == 0;
    assertEquals(expectedResult, isEvenSum);
}

Each row above produces 6 test executions (3 x-values times 2 y-values). Use value sets judiciously — the number of test cases grows multiplicatively with each additional set.

Sets as Parameters

When the parameter is declared as a Set type, the entire set is passed as a single argument without expansion:

@TableTest("""
    Values       | Size?
    {1, 2, 3}    | 3
    {a, b, c, d} | 4
    {}           | 0
    """)
void testSetParameter(Set<String> values, int expectedSize) {
    assertEquals(expectedSize, values.size());
}

Comments and Blank Lines

Lines starting with // (ignoring leading whitespace) are treated as comments and ignored. Blank lines are also ignored. Both can be used to organise and annotate your tables.

@TableTest("""
    String      | Length?
    
    Hello world | 11
    
    // The next row is currently disabled
    // "World, hello" | 12
    
    // Special characters must be quoted
    '|'         | 1
    '[:]'       | 3
    """)
void testComment(String string, int expectedLength) {
    assertEquals(expectedLength, string.length());
}

External Table Files

For large datasets, store the table in an external file using the resource attribute. The file is located as a resource relative to the test class and is typically stored in the test resources directory.

By default, the file is assumed to use UTF-8 encoding. Specify a different encoding with the encoding attribute if needed.

@TableTest(resource = "/external.table")
void testExternalTable(int a, int b, int sum) {
    assertEquals(sum, a + b);
}

A custom encoding can be specified:

@TableTest(resource = "/custom-encoding.table", encoding = "ISO-8859-1")
void testExternalTableWithCustomEncoding(String string, int expectedLength) {
    assertEquals(expectedLength, string.length());
}

JUnit-Supplied Parameters

TableTest methods can receive additional parameters supplied by JUnit (such as TestInfo, TestReporter, @TempDir, etc.). Two rules apply:

  1. Resolver-provided parameters must be declared last, after all table columns.
  2. If the table includes a scenario column, it must have an explicit parameter annotated with @Scenario — the implicit scenario detection relies on the column-to-parameter count, which resolver parameters would disrupt.
@TableTest("""
    Scenario | value | double?
    Zero     | 0     | 0
    Two      | 2     | 4
    """)
void testDoubleValue(@Scenario String scenario, int value,
                     int expectedResult, TestInfo info) {
    assertEquals(expectedResult, 2 * value);
    assertNotNull(info);
}

Escape Sequences

Escape sequence handling varies depending on the programming language.

Java Text Blocks

In Java text blocks, all Java escape sequences (\t, \", \\, \uXXXX, etc.) are processed by the Java compiler before TableTest receives the values:

@TableTest("""
    Scenario                                | Input      | Length?
    Tab character processed by compiler     | a\tb       | 3
    Quote marks processed by compiler       | Say \"hi\" | 8
    Backslash processed by compiler         | path\\file | 9
    Unicode character processed by compiler | \u0041B    | 2
    Octal character processed by compiler   | \101B      | 2
    """)
void testEscapeSequences(String input, int expectedLength) {
    assertEquals(expectedLength, input.length());
}

Java String Arrays

The same rules apply to Java string arrays — escape sequences are processed by the compiler:

@TableTest({
    "Scenario                                | Input      | Length?",
    "Tab character processed by compiler     | a\tb       | 3      ",
    "Quote marks processed by compiler       | Say \"hi\" | 8      ",
    "Backslash processed by compiler         | path\\file | 9      ",
    "Unicode character processed by compiler | \u0041B    | 2      ",
    "Octal character processed by compiler   | \101B      | 2      "
})
void testEscapeSequencesStringArray(String input, int expectedLength) {
    assertEquals(expectedLength, input.length());
}

Kotlin Raw Strings

Using Kotlin raw strings, escape sequences are not processed. They remain as literal backslash characters:

@TableTest(
    """
    Scenario                                    | Input      | Length?
    Tab character NOT processed by compiler     | a\tb       | 4
    Quote marks NOT processed by compiler       | Say \"hi\" | 10
    Backslash NOT processed by compiler         | path\\file | 10
    Unicode character NOT processed by compiler | \u0041B    | 7
    Octal character NOT processed by compiler   | \101B      | 5
    """
)
fun testEscapeSequences(input: String, expectedLength: Int) {
    assertEquals(expectedLength, input.length)
}

External Files

Table files are read as raw text independent of the programming language, meaning escape sequences are not processed and remain literal.

If you need special characters in Kotlin or external table files, use actual characters instead of escape sequences, or use Kotlin regular strings for simple cases.

Next Steps

See all these features in action: Realistic Example