Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!
Home
Glossary

Unit testing in Flutter

What is unit testing in Flutter?

Unit testing in Flutter means testing small, isolated pieces of logic such as functions, services, repositories, or ViewModels without rendering UI. A unit test verifies that a piece of Dart code behaves correctly for a given input.

In real Flutter apps, unit tests usually target asynchronous business logic rather than simple calculations.

How does it work?

Unit tests run in a Dart-only environment using flutter test. They do not require a device, emulator, or widget tree.

Most Flutter unit tests follow the Arrange → Act → Assert pattern and are often asynchronous because APIs, databases, and storage return Futures.

Example (async logic):

test('returns user name from repository', () async {
  final repo = FakeUserRepository();
  final service = UserService(repo);

  final name = await service.getUserName();

  expect(name, 'Alice');
});

Why does it matter in Flutter app development?

Flutter apps rely heavily on async logic: API calls, local storage, authentication, and state management. Unit tests allow you to verify this logic quickly without running the app.

They help you:

  • Catch bugs early in async flows
  • Refactor logic safely
  • Keep UI simple and thin
  • Make ViewModels and use cases testable

Strong unit tests reduce the need to debug complex UI issues later.

Mocking dependencies (critical)

In real apps, logic depends on APIs or databases. You should never call real services in unit tests.

Standard tools for mocking in Flutter:

  • mocktail (recommended, simpler API)
  • mockito (older but widely used)

Mocking lets you control responses and test edge cases (errors, empty data, timeouts).

Handling async code correctly

Most unit tests in Flutter are async. Always:

  • Mark the test function as async
  • Use await for Futures
  • Assert on resolved values, not Futures themselves

Failing to await is one of the most common beginner mistakes.

setUp and tearDown

To avoid shared state between tests, Flutter provides lifecycle hooks:

  • setUp() – runs before each test
  • tearDown() – runs after each test

Use them to create fresh instances of services, mocks, or ViewModels for every test case.

When to use unit testing?

Unit tests are most effective when you want to verify the correctness of isolated pieces of logic without relying on Flutter’s widget tree or platform behavior. Typical scenarios include:

  • Business rules and calculations: Validate that algorithms, formulas, or conditional logic produce the expected results.
  • Repositories and services: Test data access layers, API clients, or local database interactions (using mocks or fakes).
  • ViewModels, Cubits, and Controllers: Ensure that state transitions and business logic behave correctly, independent of the UI.
  • Data mappers and validators: Verify that data transformations, serialization, and validation rules work consistently.
  • Utility functions and pure Dart classes: Anything that does not require BuildContext or Flutter widgets is an ideal candidate for unit testing.
  • Regression protection: Unit tests quickly catch unintended changes when refactoring or adding features.

Unit tests are fast, deterministic, and easy to run in CI/CD pipelines, making them the foundation of a reliable Flutter codebase.

When not to use unit testing?

Unit tests are not suitable for anything that requires Flutter widgets, platform interaction, or full integration of multiple layers. Avoid using unit tests for:

  • UI layout, appearance, and rendering: These are better handled with widget tests, which can verify how widgets behave and respond to state changes.
  • Navigation, routing, or gesture handling: Interactions like taps, swipes, and deep links belong in integration tests, where the framework and routing stack are available.
  • Platform-specific behavior: E.g., camera access, push notifications, or geolocation, which require actual device APIs. These are covered by integration or end-to-end tests.
  • External dependencies without mocks: Calling real network APIs or databases in unit tests breaks isolation and determinism. Instead, use mocked services or fakes to keep unit tests reliable and fast.

In short, if a test needs Flutter widgets, the platform, or multiple layers to work together, it is not a unit test. Unit testing is about fast, isolated verification of logic, while everything else moves to widget or integration tests.

Best practices for unit testing

When performing unit testing in Flutter, there are several key principles and constraints you must consider to ensure tests are correct, and reliable:

  • Keep logic in pure Dart classes.
  • Inject dependencies instead of creating them.
  • Mock external services.
  • Write tests for async paths and errors.
  • Use package:flutter_test in Flutter projects.

Unit testing is the foundation of a maintainable Flutter codebase.

Common mistakes to avoid

  • Testing UI instead of logic.
  • Calling real APIs instead of mocking.
  • Forgetting await in async tests.
  • Creating state outside setUp().
  • Depending on BuildContext in testable classes.

Learn more

UI Testing in Flutter by LeanCode

The Role of UI Testing in Large-Scale Applications

We dive into the topic of UI testing in Flutter. Read more about automated UI tests' benefits and available solutions and why UI testing plays a crucial role, especially in large-scale applications.

Announcing Patrol 4.0 by LeanCode

How Patrol 4.0 Makes Cross-Platform Flutter Testing Possible

Patrol 4.0 is here! By far the biggest update since improvements were made to the test building, it brings support for a web platform, a VS Code extension, a better debugging experience, and many smaller improvements. Let’s dive into the details and the brief backstory behind this release.

Patrol Web support added by LeanCode

Simplifying Flutter Web Testing: Patrol Web

Patrol has reached version 4.0, marking a major milestone. Among the many new features, one stands out in particular: Patrol now supports Web! In this article, you’ll find a rundown of what Patrol Web can do, but also a look behind the scenes: how we designed it, why certain decisions were made, and what it took to bring Patrol’s architecture from mobile into the browser.