Skip to content

Widget Testing

In React you test components with jest and React Testing Library: render() mounts the component, screen.getByText() queries it, fireEvent.click() interacts with it. Flutter’s testing API maps almost one-to-one: testWidgets() is your test() block, pumpWidget() is render(), find.text() is screen.getByText(), and tester.tap() is fireEvent.click(). The mental model is identical — only the syntax differs.

Testing note: Widget tests require the flutter test runner — they cannot run in DartPad. Run locally with flutter test.

Flutter tests live in test/ at the project root, mirroring the way you might put tests in src/__tests__/ or colocate *.test.tsx files. The naming convention is *_test.dart — Flutter’s test runner discovers any file ending in _test.dart under test/.

Run tests with:

Terminal window
# Run all tests
flutter test
# Run a single file
flutter test test/counter_test.dart

The flutter_test package is built into the SDK — no npm install required. Import it in every test file:

import 'package:flutter_test/flutter_test.dart';

Wrap the widget under test in MaterialApp(home: Scaffold(body: ...)). This is the Flutter equivalent of wrapping your component in a Provider, ThemeProvider, or custom render wrapper in RTL — it satisfies widgets that read Theme, MediaQuery, or Navigator from their ancestor context.

React Testing LibraryFlutter widget testPurpose
render(<MyWidget />)await tester.pumpWidget(const MaterialApp(home: MyWidget()))Mount the widget
screen.getByText('Hi')find.text('Hi')Find by text
screen.getByType(Button)find.byType(ElevatedButton)Find by type
screen.getByKey('my-key')find.byKey(const Key('my-key'))Find by key
fireEvent.click(el)await tester.tap(find.byType(ElevatedButton))Tap
userEvent.type(el, 'Hello')await tester.enterText(find.byType(TextField), 'Hello')Type text
await waitFor(...)await tester.pumpAndSettle()Wait for animations/async
expect(el).toBeInTheDocument()expect(find.text('Hi'), findsOneWidget)Assert presence
expect(el).not.toBeInTheDocument()expect(find.text('Hi'), findsNothing)Assert absence
React + RTL
// React + React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('increments count on button click', () => {
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
Flutter testWidgets
// Flutter testWidgets
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter.dart';
void main() {
testWidgets('increments count on button tap', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: Counter())),
);
expect(find.text('Count: 0'), findsOneWidget);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.text('Count: 1'), findsOneWidget);
});
}
What is the Flutter equivalent of RTL's render() function?
What does await tester.pumpAndSettle() do?
Which matcher asserts that a widget is NOT present in the tree?
Why must widgets under test be wrapped in MaterialApp?