Test Matchers
All available test matchers
Matchers verify terminal output in tests. This reference lists all available matchers.
containsText
Check if text appears anywhere in the terminal:
expect(tester.terminalState, containsText('Hello'));
This searches the entire terminal for the text.
hasTextAt
Check text at a specific position:
expect(tester.terminalState, hasTextAt(row, col, 'Text'));
Example:
// Check title at row 5, column 10
expect(tester.terminalState, hasTextAt(5, 10, 'Title'));
Positions are 0-indexed.
hasStyledText
Check text with specific styling:
expect(
tester.terminalState,
hasStyledText(
'Error',
color: Colors.red,
bold: true,
),
);
Options:
color: Text colorbackgroundColor: Background colorbold: Bold textitalic: Italic textunderline: Underlined text
matchesSnapshot
Compare against saved snapshot:
expect(tester.terminalState, matchesSnapshot('my-component'));
First run creates the snapshot file. Subsequent runs compare against it.
Update snapshots:
dart test --update-snapshots
Combining matchers
Use standard Dart matchers:
// Multiple texts
expect(tester.terminalState, containsText('Hello'));
expect(tester.terminalState, containsText('World'));
// Negation
expect(tester.terminalState, isNot(containsText('Error')));
// Any of
expect(
tester.terminalState,
anyOf([
containsText('Loading'),
containsText('Done'),
]),
);
Testing text display
test('displays welcome message', () async {
await testNocterm('welcome', (tester) async {
await tester.pumpComponent(WelcomeScreen());
expect(tester.terminalState, containsText('Welcome to Nocterm'));
expect(tester.terminalState, hasTextAt(0, 0, 'Welcome'));
});
});
Testing styled text
test('shows error in red', () async {
await testNocterm('error style', (tester) async {
await tester.pumpComponent(ErrorMessage(message: 'Failed'));
expect(
tester.terminalState,
hasStyledText('Failed', color: Colors.red, bold: true),
);
});
});
Testing empty state
test('empty list shows placeholder', () async {
await testNocterm('empty', (tester) async {
await tester.pumpComponent(ItemList(items: []));
expect(tester.terminalState, containsText('No items'));
});
});
Testing layout
test('title in correct position', () async {
await testNocterm('layout', (tester) async {
await tester.pumpComponent(
Container(
margin: EdgeInsets.all(2),
child: Text('Title'),
),
);
// Title should be at row 2, col 2 (after margin)
expect(tester.terminalState, hasTextAt(2, 2, 'Title'));
});
});
Snapshot testing
test('component matches snapshot', () async {
await testNocterm('snapshot', (tester) async {
await tester.pumpComponent(ComplexComponent());
expect(tester.terminalState, matchesSnapshot('complex-component'));
});
});
Writing custom matchers
Create custom matchers for complex checks:
Matcher hasErrorBorder() {
return predicate<TerminalState>(
(state) {
// Check if border is red
return state.toString().contains('red border pattern');
},
'has error border',
);
}
// Usage
expect(tester.terminalState, hasErrorBorder());
Use the right matcher
// Good - specific check
expect(tester.terminalState, hasTextAt(5, 10, 'Title'));
// Bad - too general
expect(tester.terminalState, containsText('Title'));
Test styling when it matters
// Errors should be red
expect(tester.terminalState, hasStyledText('Error', color: Colors.red));
// Success should be green
expect(tester.terminalState, hasStyledText('Success', color: Colors.green));
Use snapshots for complex UI
For complex layouts, snapshots are easier than position checks:
expect(tester.terminalState, matchesSnapshot('dashboard'));
Write readable tests
// Good - clear intent
expect(tester.terminalState, containsText('Welcome'));
// Bad - unclear what's being tested
expect(tester.terminalState.toString(), contains('We'));
Next steps
- Writing Tests - Test basics
- Visual Testing - Debug with visual output
- Examples - See example apps