Flutter ships with testing built in. Run flutter create and project already has a test/ directory with a sample widget test. The flutter_test package is included by default. There's no setup, no extra dependencies, no configuration step before you can write your first test.
That's good news. The bad news is that Flutter's built-in testing stops at widget layer. Integration tests (E2E) require a separate package (integration_test), can't interact with native platform dialogs (permissions, notifications, system alerts), and run on emulators that don't reflect how app behaves on real devices with OEM skins and manufacturer-specific behavior.
This guide covers all four testing layers in Flutter: unit tests, widget tests, golden tests, and integration/E2E tests. Each includes a code example, what it catches, what it misses, and honest trade-off. The official Flutter testing docs cover API well. This guide covers practical decisions docs leave to you.
Why Flutter testing is different from React Native testing
Flutter doesn't use platform-native UI components. React Native renders actual iOS UIKit views and Android TextViews. Flutter renders everything itself using its own engine (Skia, and now Impeller on iOS). Every pixel on screen is drawn by Flutter, not by OS.
This changes testing equation in two ways.
Widget tests are more accurate than component tests in React Native. Because Flutter renders its own widgets (not native views), a widget test in Flutter actually renders widget tree, measures layout, and paints frames. It's closer to what user sees than a React Native component test running in Node.js. You can test tap targets, overflow, text truncation, and layout behavior inside a widget test without needing a device.
Device fragmentation shows up differently. On React Native, a <Text> component renders via iOS UIKit on iPhone and Android TextView on Samsung, and each platform handles line-breaking and font metrics differently. In Flutter, text rendering is same across all devices because Flutter does it. But OEM-specific behavior still matters: Samsung's gesture navigation bar changes inset heights, Xiaomi's security popup blocks app on first launch, and battery optimization on various OEMs kills background processes. These are OS-level behaviors that Flutter can't control, and they only surface on real devices.
Layer 1: unit tests
Unit tests verify pure Dart logic. Functions, classes, state management. No widgets, no rendering, no framework.
What it catches: Logic errors in your Dart code. The discount going negative. The total not updating when items are removed. A currency formatter producing $NaN for edge cases.
What it misses: Everything visual. A unit test doesn't know whether total renders on screen, whether font is readable, or whether "Checkout" button is tappable.
Run with flutter test test/cart_test.dart. Runs in milliseconds. This is where you want bulk of your tests.
Layer 2: widget tests
Widget tests render actual Flutter widgets, pump frames, simulate taps and text input, and verify widget tree. Because Flutter owns rendering engine, widget tests are closer to reality than component tests in other frameworks.
What it catches: Rendering bugs, missing validation messages, broken tap handlers, layout overflow. The test above catches if someone removes email validation or disconnects button's onPressed handler.
What it misses: Platform-specific behavior. Widget tests run on your development machine, not on a device. They can't test how app interacts with OS (permissions, notifications, background behavior), how it renders on specific screen sizes and densities in real world, or how third-party native plugins behave.
Layer 3: golden tests
Golden tests (also called screenshot tests) capture a pixel-perfect image of a widget and compare it against a saved baseline. If rendering changes, test fails.
Run flutter test --update-goldens to generate baseline. On subsequent runs, test compares rendered output to saved image. A font change, a padding tweak, or a color shift will cause a pixel diff and fail test.
What it catches: Visual regressions that widget tests miss. A widget test confirms "Profile Card" renders. A golden test catches that someone changed card's border radius from 12px to 8px.
What it misses: Device-specific rendering. Golden tests run on host machine with a fixed logical resolution. They can't tell you how widget looks on a 720p Samsung screen with system font set to "Large."
The catch: Golden tests are fragile across CI environments. A font rendering difference between macOS and Linux can produce a pixel diff that fails test even though nothing changed in code. Pin your CI runner's OS and Flutter version to avoid phantom failures.
Layer 4: integration tests (E2E)
Integration tests run full app on a device or emulator. They test complete user flows: launch, login, browse, checkout, confirm.
Flutter's integration_test package is official tool. Tests are written in Dart, run inside app process, and interact with widget tree directly.
The limitation that matters: integration_test can't interact with native platform UI. As official docs note, "this package can't interact with native platform UI, such as permission dialogs, notifications, or platform views." If your app uses native permissions, push notifications, biometric authentication, or any plugin that renders a native dialog, integration_test can't tap through it.
Patrol is open-source answer. Built by LeanCode, Patrol extends Flutter's testing with native interaction: dismiss permission dialogs, toggle Wi-Fi, handle notifications. It writes in Dart and runs on real devices. It's closest thing to Detox for React Native that Flutter has.
But both run on emulators by default. And emulators don't reproduce Samsung's One UI insets, Xiaomi's HyperOS security popup, manufacturer font scaling, or real GPU performance. A test that passes on a stock Android emulator might fail on a Galaxy A14 because gesture navigation bar covers bottom of screen.
For real-device E2E across OEM matrix, Drizz tests Flutter apps same way it tests any mobile app. Tests are written in plain English ("Tap 'Log in,'" "Type 'user@test.com' in email field," "Validate 'Welcome' is visible"). Vision AI reads screen visually, so it works with Flutter's custom rendering engine without needing access to widget tree. Tests run on real Android and iOS devices. The popup agent handles OEM dialogs automatically. Self-healing adapts when layout changes across devices. Teams go from 15 tests per month to 200, with flakiness at ~5%.
How layers fit together
On every commit: Unit tests (fast, pure Dart logic) and widget tests (rendering, interaction). These run in seconds with flutter test.
On every PR: Golden tests (visual regression on stable components). Pin CI environment to avoid cross-platform pixel diffs.
Before every release: Integration tests on real devices across device matrix. Either Patrol for native dialog handling or Drizz for plain-English tests on real OEM hardware.
The Flutter docs summarize it well: "A well-tested app has many unit and widget tests, tracked by code coverage, plus enough integration tests to cover all important use cases."
FAQ
What testing frameworks does Flutter support?
Flutter includes flutter_test for unit and widget tests, integration_test for E2E, and supports golden tests natively. Third-party options include Patrol for native interactions, Mockito for mocking, and Drizz for real-device E2E.
Can I test Flutter apps on real devices?
Yes. integration_test runs on emulators and physical devices. Patrol adds native dialog handling. Drizz runs plain-English tests on real Android and iOS devices using Vision AI, covering OEM-specific behavior that emulators miss.
What is golden testing in Flutter?
Golden testing captures a pixel-perfect screenshot of a widget and compares it against a saved baseline. It catches visual regressions (padding changes, color shifts, font updates) that widget tests miss.
Is Flutter testing easier than React Native testing?
In some ways, yes. Flutter's widget tests are more accurate because Flutter renders everything itself (no native view translation). But for E2E, Flutter faces same real device gap: integration_test can't handle native dialogs, and emulators miss OEM behavior.
How do I mock dependencies in Flutter tests?
Use Mockito (mockito package) for mocking classes and services. For HTTP, use http_mock_adapter or nock. Inject dependencies via constructors or a DI framework like Riverpod or Provider so they're easy to swap in tests.
What is difference between widget tests and integration tests?
Widget tests render a single widget in isolation (fast, no device needed). Integration tests run full app on a device or emulator and test complete user flows (slow, real environment). Widget tests catch rendering bugs. Integration tests catch cross-screen and native interaction bugs.
‍


