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 testrunner — they cannot run in DartPad. Run locally withflutter test.
Test setup
Section titled “Test setup”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:
# Run all testsflutter test
# Run a single fileflutter test test/counter_test.dartThe 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.
Core API mapping
Section titled “Core API mapping”| React Testing Library | Flutter widget test | Purpose |
|---|---|---|
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 + React Testing Libraryimport { 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 testWidgetsimport '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); });}