How to Test Biometric Login on Flutter (Complete Guide)

How to Test Biometric Login on Flutter (Complete Guide) starts with understanding why biometric authentication matters for Flutter apps. Modern users expect fast, secure sign‑in methods, and Flutter’s

May 17, 2026 · 14 min read · How-To Guides

How to Test Biometric Login on Flutter (Complete Guide) starts with understanding why biometric authentication matters for Flutter apps. Modern users expect fast, secure sign‑in methods, and Flutter’s platform‑channel plugins let you call Android’s BiometricPrompt or iOS’s LocalAuthentication with a single Dart API. When the integration is weak, production crashes, false‑negatives, or privacy leaks appear, eroding trust and triggering store rejections. This guide walks you through a complete test matrix, manual steps, automated strategies, tooling, and how autonomous, persona‑driven exploration surfaces bugs that scripted tests miss.

How to Test Biometric Login on Flutter (Complete Guide): Why It Matters

Biometric login is no longer a nice‑to‑have; it is a feature; it is often the primary factoring. A broken flow can cause:

Flutter’s local_auth plugin abstracts the native APIs, but the abstraction adds layers where things can go wrong:

  1. Platform channel misuse – forgetting to await the result or ignoring error codes.
  2. Incorrect use of authenticateWithBiometrics vs authenticateWithBiometricsOrDeviceCredentials – mixing up policies leads to unexpected PIN prompts.
  3. Missing error handling – not distinguishing BiometricError.authenticationFailed from BiometricError.lockedOut.
  4. UI thread violations – showing dialogs from a non‑UI thread causing a black screen.
  5. State mismanagement – letting the login widget rebuild while the biometric prompt is active, resulting in overlapping dialogs.

Understanding these failure modes shapes the test matrix that follows.

How to Test Biometric Login on Flutter (Complete Guide): Test Matrix

A systematic matrix ensures you cover happy paths, error conditions, accessibility, and security. Below is a comprehensive table you can copy into a test plan spreadsheet.

CategoryIDDescriptionPreconditionsStepsExpected ResultPass/Fail Criteria
Happy PathHP1Successful fingerprint loginDevice has enrolled fingerprint, app granted biometric permission1. Navigate to login screen 2. Tap “Use Biometrics” 3. Place enrolled finger on sensorBiometric prompt appears, authentication succeeds, user lands on home screenPASS if home screen loads within 2 s, no error dialog
Happy PathHP2Successful face login (iOS)Face ID enrolled, permission grantedSame as HP1 but using facePrompt shows Face ID UI, success, home screenPASS if no fallback PIN appears
Error PathEP1Biometric sensor not availableNo fingerprint/face enrolled, biometric hardware presentTap biometric buttonDialog shows “Biometric not available”, offers PIN/password fallbackPASS if fallback offered and works
Error PathEP2User cancels promptSensor availableTap biometric button, then press cancel in promptPrompt dismisses, app shows “Authentication cancelled”, remains on login screenPASS if app stays on login, no crash
Error PathEP3Too many failed attempts (lockout)Simulate 5 failed attempts (Android) or 5 failed Face ID attempts (iOS)Repeatedly place wrong finger or look awayAfter threshold, biometric disabled, prompt shows “Too many attempts”, offers device credentialsPASS if lockout respected and fallback works
Edge CaseED1App in background when biometric invokedApp minimized, biometric button still visible (e.g., via notification)Tap biometric button from notificationSystem brings app to foreground, prompt appears, behaves as HP1PASS if no crash and prompt works
Edge CaseED2Screen orientation change during promptStart biometric auth, rotate device while prompt showingRotate devicePrompt remains visible, no UI tearing, auth result delivered correctlyPASS if auth completes without visual glitch
Edge CaseED3Low memory conditionRun app on device with < 500 MB free RAM, start biometric flowTrigger biometric buttonApp does not OOM, prompt appears, authentication worksPASS if no OutOfMemoryError
AccessibilityAC1TalkBack/VoiceOver labelAccessibility service enabledNavigate to biometric button, activate TalkBackButton announces “Use biometric login, button”PASS if label present and descriptive
AccessibilityAC2Contrast ratioHigh contrast theme enabledInspect biometric button colorsContrast ≥ 4.5:1 (AA) for normal textPASS if meets WCAG AA
AccessibilityAC3Touch target sizeEnable switch controlMeasure biometric button hit areaMinimum 48 dp × 48 dpPASS if meets guideline
SecuritySE1Raw biometric data not storedDevice with root/jailbreak detection disabledAfter successful auth, inspect app’s private storage (via adb run-as or Xcode device logs)No biometric template or raw sensor data foundPASS if only encrypted keys or tokens stored
SecuritySE2Key invalidation on biometric changeEnroll a new fingerprint after successful authAttempt to use existing encrypted tokenToken invalidated, app prompts for re‑authenticationPASS if old token rejected
SecuritySE3Resistance to replay attackCapture biometric auth intent via Frida or similar, replaySend captured intent to appApp rejects replay, shows authentication failurePASS if replay blocked
PrivacyPR1Permission rationale shownFirst‑time launch, biometric permission not grantedAttempt to use biometric buttonSystem shows permission rationale dialog defined in AndroidManifest.xml or Info.plistPASS if rationale appears and is concise
PrivacyPR2No biometric data in logsEnable verbose logging, attempt authSearch logs for keyword “fingerprint” or “face”No raw biometric data appearsPASS if logs contain only result codes

Each row maps to a concrete test you can automate or perform manually. The matrix is deliberately exhaustive; you can prune low‑risk rows for rapid regression cycles but keep the core happy‑path, error‑path, accessibility, and security rows for every release.

How to Test Biometric Login on Flutter (Complete Guide): Manual Testing Approach

Manual testing remains valuable for exploratory checks, especially for edge cases that depend on device state or user perception. Follow this step‑by‑step checklist on a physical device (emulators often lack genuine biometric hardware).

Setup

  1. Enroll at least one biometric credential (fingerprint or face) on the device.
  2. Grant the app biometric permission – on Android this is runtime; on iOS it’s prompted at first use.
  3. Install a debug build that enables verbose logging (flutter run --verbose).
  4. Prepare a fallback PIN/password in the app settings to verify error paths.

Execution

StepActionObservation
1Launch the app, navigate to the login screen.Login UI visible, biometric button enabled.
2Tap the biometric button.System biometric prompt appears (fingerprint icon or Face ID animation).
3Present a valid biometric.Prompt dismisses, app transitions to home screen within 2 s.
4Return to login screen (via logout or back).Biometric button still enabled.
5Tap biometric button, then cancel the prompt.Prompt disappears, login screen shows “Authentication cancelled”, app stays on login.
6Repeatedly present an invalid biometric (wrong finger, look away) until lockout threshold is reached.After configured attempts, prompt shows lockout message, offers device credentials.
7Enter correct device PIN/password.App logs in successfully, confirming fallback works.
8Minimize the app, then trigger biometric login via a notification deep‑link.App restores to foreground, prompt appears, authentication succeeds.
9Rotate device while prompt is visible.Prompt stays centered, no clipping, auth result delivered.
10Enable TalkBack (Android) or VoiceOver (iOS). Focus on biometric button.Audio label reads “Use biometric login, button”.
11Switch to high‑contrast mode. Verify button contrast with a color‑contrast analyzer.Ratio ≥ 4.5:1.
12Use adb shell am get-debug-app (Android) or Console.app (iOS) to confirm no biometric raw data appears in logs.Logs contain only result codes like BiometricResult.success.
13(Optional) Root the device or jailbreak, attempt to pull /data/data//shared_prefs/ or Library/Preferences. Verify no biometric templates stored.Only encrypted keys or auth tokens present.
14(Optional) Use Frida to inject a script that replays the last successful biometric intent. Observe app reaction.App rejects replay, shows failure.

Notes

Manual testing catches subtle UI glitches, accessibility oversights, and device‑specific quirks that automated scripts may miss if they rely solely on mock platform channels.

Automated Testing Strategies for Flutter Biometric Login

Automation speeds regression and CI validation. Because the biometric prompt is native, you must either mock the platform channel or use real device farms that expose biometric simulation.

1. Mock the local_auth Plugin

The most common approach is to provide a fake LocalAuthentication instance via dependency injection. Below is a minimal example using mockito.


// auth_service.dart
abstract class AuthService {
  Future<bool> authenticate({
    String? localizedReason,
    bool useErrorDialogs = true,
    bool stickyAuth = false,
  });
}

// real_auth_service.dart
import 'package:local_auth/local_auth.dart';

class RealAuthService implements AuthService {
  final LocalAuthentication _auth = LocalAuthentication();

  @override
  Future<bool> authenticate({
    String? localizedReason,
    bool useErrorDialogs = true,
    bool stickyAuth = false,
  }) async {
    final bool didAuthenticate = await _auth.authenticate(
      localizedReason: localizedReason ?? 'Sign in',
      useErrorDialogs: useErrorDialogs,
      stickyAuth: stickyAuth,
    );
    return didAuthenticate;
  }
}

// fake_auth_service.dart
import 'package:mockito/mockito.dart';

class FakeAuthService extends Mock implements AuthService {}

In your test, you configure the fake to return specific results:


// biometric_login_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

void main() {
  late FakeAuthService fakeAuth;
  setUp(() {
    fakeAuth = FakeAuthService();
  });

  testWidgets('successful fingerprint login navigates to home', (tester) async {
    when(fakeAuth.authenticate(any)).thenAnswer((_) async => true);

    await tester.pumpWidget(
      Provider<AuthService>.value(
        value: fakeAuth,
        child: const LoginPage(),
      ),
    );

    await tester.tap(find.byIcon(Icons.fingerprint));
    await tester.pumpAndSettle();

    expect(find.text('Welcome'), findsOneWidget);
    verify(fakeAuth.authenticate(any)).called(1);
  });

  testWidgets('user cancels prompt shows error', (tester) async {
    when(fakeAuth.authenticate(any)).thenAnswer((_) async => false);

    await tester.pumpWidget(
      Provider<AuthService>.value(
        value: fakeAuth,
        child: const LoginPage(),
      ),
    );

    await tester.tap(find.byIcon(Icons.fingerprint));
    await tester.pump();

    expect(find.text('Authentication cancelled'), findsOneWidget);
  });
}

Pros: Fast, deterministic, runs on any CI agent.

Cons: Does not validate native plugin integration, UI thread behavior, or permission handling.

2. Use the local_auth Plugin’s Built‑in Test Mode (Android Only)

Starting with version 0.7.0, the plugin exposes a setMockLocalAuthentication method for instrumentation tests. This lets you drive the real plugin but feed it simulated biometric results.

Add to android/app/src/androidTest/java/com/example/app/MainActivityFlutterTest.java:


@Rule
public FlutterActivityTestRule<?> rule = new FlutterActivityTestRule<>(MainActivity.class);

@Test
public void biometricSuccessTest() {
  // Enable mock mode
  LocalAuthenticationPlugin.setMockLocalAuthentication(true);

  // Simulate a successful fingerprint
  LocalAuthenticationPlugin.setMockLocalAuthenticationResult(true);

  // Launch the app
  rule.launchActivity(null);

  // Tap biometric button
  onView(withId(R.id.biometric_button)).perform(click());

  // Wait for home screen
  onView(withText("Welcome")).check(matches(isDisplayed()));
}

Pros: Tests real plugin code path, respects threading, and surface‑level UI.

Cons: Requires Android instrumentation tests; iOS lacks an equivalent mock mechanism.

3. Real Device Farm with Biometric Simulation

Services like Firebase Test Lab, AWS Device Farm, or Sauce Labs let you upload an APK/IPA and run scripts that interact with the actual biometric HAL via adb commands:

Example Bash snippet for Firebase Test Lab:


gcloud firebase test android run \
  --type instrumentation \
  --app app-debug.apk \
  --test biometric_test.apk \
  --device model=Pixel3,version=30,locale=en,orientation=portrait \
  --environment-variables fingerprintId=1

Inside the test:


@Before
public void enrollFingerprint() {
  // Enroll a fake fingerprint id 1 for the test session
  DeviceUtils.enrollFingerprint(1);
}

@Test
public void testBiometricLogin() {
  onView(withId(R.id.biometric_button)).perform(click());
  // Simulate finger press
  DeviceUtils.triggerFingerprint(1);
  onView(withText("Welcome")).check(matches(isDisplayed()));
}

Pros: Closest to production reality; catches native crashes, ANRs, and permission flows.

Cons: Slower, higher cost, requires device‑farm access.

4. Hybrid Approach: Unit + Integration

A practical pipeline:

  1. Unit tests with mocked AuthService for business logic (state transitions, error handling).
  2. Widget tests that inject a FakeAuthService to verify UI reacts correctly to success/failure/cancel.
  3. Instrumentation tests (Android) or UI tests (XCTest) on a device farm using the real plugin with mock biometric simulation to validate native integration.
  4. Periodic manual exploratory runs on a matrix of physical devices to catch device‑specific regressions.

This layered strategy gives fast feedback while retaining confidence in the native layering Tooling and Libraries for Biometric Testing in Flutter

Tool/LanguageNotes

Autonomous, Persona‑Driven Exploration with SUSA

While scripted tests verify expected paths, autonomous exploration can surface unexpected states—especially when biometric flows intersect with app navigation, deep links, or background processes. SUSA (SUSATest) is an autonomous QA platform that explores an uploaded APK or web URL using a variety of user personas, each with distinct behavior patterns, and reports crashes, ANRs, dead buttons, accessibility issues, and UX friction.

How SUSA Approaches Biometric Login

  1. Persona‑Based Interaction
  1. Exploration Mechanics

SUSA instruments the Flutter app via method‑channel hooks, records every UI state (route, widget tree, accessibility labels), and builds a transition graph. When it encounters the biometric login screen, it:

  1. Bug Detection Specific to Biometrics

Integrating SUSA into Your CI

  1. Upload APK/AABsusatest-agent upload app-release.apk.
  2. Define persona set – use the default set or create a custom JSON focusing on biometric scenarios (e.g., add “biometric‑power‑user” that attempts a purchase right after auth).
  3. Run explorationsusatest-agent run --personas novice,impatient,accessibility,adversarial --duration 15m.
  4. Retrieve report – the agent returns a JSON with crashes, ANRs, accessibilityViolations, and flowResults. Each flow includes a PASS/FAIL verdict for login, signup, checkout, etc.
  5. Gate on failures – fail the build if any flowResult for the biometric login path is FAILED or if new accessibilityViolations appear.

Because SUSA learns from each run, repeated executions explore deeper paths (e.g., logging in, then navigating to settings to change biometric preference, then logging out) without you writing additional test code.

Checklist for Biometric Login Testing in Flutter

Use this concise list before every release. Mark each item as Done or Blocked.

AreaItemHow to Verify
Happy PathFingerprint login succeeds and navigates to target screenManual tap + valid biometric; automated widget test with mock success
Face ID login succeeds (iOS)Same as above on iOS device
Error PathsBiometric unavailable shows fallbackDisable biometric in settings; tap button; verify fallback prompt
User cancel handled gracefullyTap button then cancel; verify “Authentication cancelled” toast
Lockout after N failures triggers fallbackSimulate failures (wrong finger/face) until lockout; verify fallback offered
Background invocation worksMinimize app, trigger biometric via notification deep‑link; verify prompt appears
Orientation change during prompt does not break UIStart auth, rotate device, verify prompt stays centered and auth completes
AccessibilityButton has descriptive labelEnable TalkBack/VoiceOver; verify announcement
Contrast meets WCAG AAUse contrast checker on button in normal and high‑contrast themes
Touch target ≥ 48 dpInspect layout or use UI Automator to measure hit‑area
SecurityNo raw biometric data storedInspect app private storage after auth; confirm only encrypted keys/tokens
Key invalidated on biometric changeEnroll new fingerprint, attempt to use old token; verify rejection
Resistant to replay attackUse Frida/adb to capture and replay auth intent; verify failure
PrivacyPermission rationale displayedFirst‑launch attempt; verify system dialog shows custom rationale
No biometric data in logsRun with verbose logging; grep for fingerprint/face terms; ensure none appear
PerformanceAuthentication completes < 2 s on median deviceMeasure with Stopwatch in test or SUSA performance metrics
No ANR during promptMonitor CPU thread traces; ensure main thread not blocked > 5 s
RegressionWidget test suite passesRun flutter test on widget and unit tests
Instrumentation test suite passes (Android)Run ./gradlew connectedAndroidTest
Device‑farm smoke test passesRun a short Firebase Test Lab or AWS Device Farm job
SUSA exploration returns PASS for login flowRun susatest-agent with biometric‑focused personas; check flowResult

If any item is Blocked, investigate and fix before promoting the build to staging or production.

Takeaways and Next Steps

Biometric login is a high‑impact feature that blends platform‑specific security with Flutter’s cross‑platform UI. Testing it demands a layered strategy:

By following the guide above, you will ship a biometric login experience that is fast, reliable, accessible, and resilient to the kinds of bugs that slip through scripted tests alone. Happy testing!

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free