Learn Flutter - Testing & Quality Assurance
Episode 13 of 23

Learn Flutter - Testing & Quality Assurance

This episode builds confidence in your code: unit testing Dart logic with flutter_test, widget testing and golden tests, integration testing with flutter_test and flutter drive, and continuous testing and test automation to keep quality high on every change.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

An app that grows without tests is a time bomb: one small change can break an unexpected feature. Episode 13 builds a safety layer through structured testing — from the smallest unit tests to integration tests that run a real app.

We discuss unit testing Dart logic, widget testing and golden tests, integration testing with flutter_test and flutter drive, and continuous testing and test automation strategies.

Unit Testing Dart Logic

The Basic Test Structure

Unit tests verify pure logic — models, repositories, calculations — without UI. All Flutter tests use the flutter_test package:

A simple unit test
import 'package:flutter_test/flutter_test.dart';
 
int tambah(int a, int b) => a + b;
 
void main() {
  test('menjumlahkan dua angka', () {
    expect(tambah(2, 3), 5);
  });
}

test defines a test case and expect verifies the result. This structure is the foundation of the entire test suite — everything uses the test and expect pattern.

Group and Descriptions

For many tests, use group to keep output organized:

Test with group
group('Calculator', () {
  test('mengembalikan 5 untuk 2 + 3', () {
    expect(tambah(2, 3), 5);
  });
 
  test('mengembalikan 0 untuk angka negatif seimbang', () {
    expect(tambah(-1, 1), 0);
  });
});

group wraps several tests into one category. Run all tests with:

Run all tests
flutter test

flutter test runs every _test.dart file in the test/ folder. Every change to business logic should come with an updated test.

Widget Testing and Golden Tests

Widget Tests: Testing the UI in Isolation

Widget tests run the UI in a simulated environment without a device:

A simple widget test
testWidgets('menampilkan nilai awal', (tester) async {
  await tester.pumpWidget(const MaterialApp(home: CounterScreen()));
 
  expect(find.text('Nilai: 0'), findsOneWidget);
 
  await tester.tap(find.byType(FloatingActionButton));
  await tester.pump();
 
  expect(find.text('Nilai: 1'), findsOneWidget);
});

tester.pumpWidget mounts the widget, tester.tap simulates a tap, and tester.pump() processes the rebuild. findsOneWidget verifies the presence of the widget being sought.

Golden Tests

A golden test compares a widget's render against a reference image (snapshot):

A simple golden test
testWidgets('sesuai golden', (tester) async {
  await tester.pumpWidget(const MaterialApp(home: HomeScreen()));
  await expectLater(
    find.byType(HomeScreen),
    matchesGoldenFile('goldens/home.png'),
  );
});

matchesGoldenFile('goldens/home.png') compares the view against the reference file. When a design legitimately changes, update the golden by running the test with the --update-goldens flag. Golden tests make sure even the smallest invisible change doesn't slip through.

Integration Testing

integration_test for Full Flows

Integration tests run the app on a device or emulator and test end-to-end flows:

Add integration_test
dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test:
    sdk: flutter

Add integration_test from the SDK to dev_dependencies. Tests are written in the integration_test/ folder:

An integration test for the login flow
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
testWidgets('login berhasil', (tester) async {
  app.main();
  await tester.pumpAndSettle();
 
  await tester.enterText(find.byKey(const Key('email')), 'user@example.com');
  await tester.tap(find.byKey(const Key('login_button')));
  await tester.pumpAndSettle();
 
  expect(find.text('Selamat datang'), findsOneWidget);
});

app.main() runs the real app, and pumpAndSettle() waits for all animations to finish. find.byKey uses a Key defined in the widget — an important reason to give Keys to key interactive elements.

flutter drive for Execution on a Device

Run integration tests on a device:

Run integration tests
flutter test integration_test -d linux

flutter test integration_test -d linux runs the entire integration test folder on the specified device. The -d target can be an emulator, physical device, or desktop.

Continuous Testing and Test Automation

Testing on Every Commit

Run the test suite in CI before merging:

Lint and test in one command
flutter analyze && flutter test

flutter analyze && flutter test combines static analysis and the full test suite. This pattern must exist in the CI pipeline — in this series, the release flow in episode 14 and the runbooks in episode 19 will build on the same foundation.

Measuring Coverage

flutter test --coverage produces a coverage report. A realistic target isn't absolute 100 percent, but covering critical logic: validation, auth, and payments. Prioritize tests that catch high-cost regressions.

Conclusion

Key takeaways:

  • Unit tests verify pure logic with test and expect.
  • flutter test runs the whole suite in the test/ folder.
  • Widget tests simulate taps and verify the UI without a device.
  • Golden tests compare the view against a reference snapshot.
  • Integration tests run a real app with flutter_test and integration_test.
  • Automate with flutter analyze && flutter test in CI.

In the next episode 14 we discuss deployment and release management — building release app bundles for Android and iOS, code signing, provisioning profiles, and app store submission, web deployment and desktop packaging, and release channels and versioning. Your app is ready to be shipped to users.

Learn Flutter - Testing & Quality Assurance | Learn Flutter