Test Matchers

All available test matchers

Matchers verify terminal output in tests. This reference lists all available matchers.

Text 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 color
  • backgroundColor: Background color
  • bold: Bold text
  • italic: Italic text
  • underline: Underlined text

State matchers

isEmpty

Check if terminal is empty:

expect(tester.terminalState, isEmpty);

isNotEmpty

Check if terminal has content:

expect(tester.terminalState, isNotEmpty);

Snapshot testing

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'),
  ]),
);

Examples

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());

Best practices

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