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] ZeroExplicit 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, 200Value 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:
- Resolver-provided parameters must be declared last, after all table columns.
- 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