A test suite earns its place when it catches a change that would frustrate a real user. Start with the flows where a mistake is expensive: signing in, saving work, submitting an order, or recovering after a network failure. A coverage percentage alone does not tell you whether those flows are protected.
Put each check at the right level
Flutter distinguishes unit tests for a function or class, widget tests for interface behavior, and integration tests for larger working flows. Use the smallest level that can prove the rule. Price calculations do not need a device; checking an actual platform permission flow often does.
Make the inputs explicit
A view model with a repository dependency can use a fake that returns success, failure, or a manually controlled future. That gives you precise control over loading and retry behavior. Avoid making most tests depend on a live backend or an arbitrary delay.
import 'package:flutter_test/flutter_test.dart';
bool canSubmit(String title) => title.trim().isNotEmpty;
void main() {
test('blank titles cannot be submitted', () {
expect(canSubmit(' '), isFalse);
});
test('a meaningful title can be submitted', () {
expect(canSubmit('Release notes'), isTrue);
});
}
This minimal example illustrates behavior-focused names. In a feature, test the real validator instead of copying it into a test file. Include the boundary that previously failed, such as whitespace-only input, and one normal success case.
Test recovery as carefully as success
- A save button becomes available again after a failed request.
- Retry preserves the text the user already entered.
- An obsolete search response does not replace newer results.
- Signing out clears account-specific content.
- Large text does not hide the main action.
Keep integration flows small
A single script that signs in, edits ten features, changes language, and signs out is difficult to diagnose when it fails. Prefer a few critical journeys with controlled test data. Reset only records owned by that test, and never point destructive test cleanup at production.
Run fast checks on each change and schedule device-dependent checks according to the project's risk and release cadence. When a regression escapes, add a focused test that demonstrates its cause before expanding the suite with unrelated checks.
Official references: Flutter testing overview and Flutter unit testing introduction.
