Chapters

Hide chapters

Flutter Apprentice

Fourth Edition · Flutter 3.16.9 · Dart 3.2.6 · Android Studio 2023.1.1

Section II: Everything’s a Widget

Section 2: 5 chapters
Show chapters Hide chapters

Section IV: Networking, Persistence & State

Section 4: 6 chapters
Show chapters Hide chapters

18. Widget Testing
Written by Alejandro Ulate Fallas

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Widget testing is about making your Flutter widgets dance to your tune. It’s essential to ensure your UI components not only look good but also work as you intended. In this chapter, you’ll:

  • Learn the concept of widget testing.
  • Load mock data into the widget tests.
  • Ensure that each ingredient displays correctly.
  • Understand what golden tests are.
  • Add golden tests to verify your widget’s look and feel.

Learning About Widget Tests

Widget testing plays an important role in ensuring your widgets’ reliability and proper functioning. Unlike other testing approaches, widget tests are specifically designed to concentrate on the interaction and underlying logic of the UI elements.

In essence, they act as “unit” tests for your widgets, providing a targeted examination of their behavior, responsiveness and functionality in isolation. This focus helps you identify and address issues early in the development process, contributing to a more robust and error-resistant Flutter application.

Some common scenarios you might want to use widget tests for are:

  • Successful Widget Building: Widget tests are particularly handy for ensuring your widgets build successfully under expected conditions.
  • User Interaction Verification: These tests enable you to simulate user actions, such as tapping or inputting text, ensuring that your widgets respond as expected.
  • State Changes and UI Updates: These tests can also help you confirm that state changes within your widgets work as intended and that these changes are reflected in the user interface.
  • Navigation and Routing Logic: By simulating navigation events, you can verify that your app transitions between screens correctly and that the UI adapts as expected.

Widget tests improve the reliability of your app. They validate critical parts of it, such as building widgets, user interaction, state management, and navigation logic.

It’s time you start working on your own widget tests. Start by adding a new test file that matches the following path test/ui/widgets/ingredient_card_test.dart. You’ll need to create the corresponding directories too.

Then, just so our test suite doesn’t fail, add the following code to your test file:

void main() {}

Use your IDE to run your tests. The result should match the screenshot below:

Adding Your First Widget Test

As you recall, a good scenario for you to get your hands on testing is to verify that the widget builds successfully. This way, you can ensure that your widget’s structure matches your expectations.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:recipes/ui/widgets/ingredient_card.dart';
testWidgets('IngredientCard can build', (WidgetTester tester) async {
  // TODO: Arrange
  // TODO: Act
  // TODO: Assert
});
// 1.
const mockIngredientName = 'colby jack cheese';
await tester.pumpWidget(
  // 2.
  MaterialApp(
    home: Scaffold(
      body: ListView(
        children: [
          // 3.
          IngredientCard(
            name: mockIngredientName,
            initiallyChecked: false,
            evenRow: true,
            onChecked: (isChecked) {},
          ),
        ],
      ),
    ),
  ),
);
final cardFinder = find.byType(IngredientCard);
final titleFinder = find.text(mockIngredientName);
expect(cardFinder, findsOneWidget);
expect(titleFinder, findsOneWidget);

Testing IngredientCard’s Behaviors

Widget testing becomes super useful when you want to check how your widgets respond to users. You can use these tests to pretend to be a user, clicking buttons or entering information. This way, you make sure your widgets react the right way and give users a smooth experience.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:recipes/ui/widgets/ingredient_card.dart';

// 1.
Widget _buildWrappedWidget(Widget child) {
  return MaterialApp(
    home: Scaffold(
      body: ListView(
        children: [
          child,
        ],
      ),
    ),
  );
}

void main() {
  // 2.
  const mockIngredientName = 'colby jack cheese';
  group('IngredientCard', () {
    testWidgets('can build', (tester) async {
      // 3.
      await tester.pumpWidget(
        _buildWrappedWidget(IngredientCard(
          name: mockIngredientName,
          initiallyChecked: false,
          evenRow: true,
          onChecked: (isChecked) {},
        )),
      );

      final cardFinder = find.byType(IngredientCard);
      final titleFinder = find.text(mockIngredientName);

      expect(cardFinder, findsOneWidget);
      expect(titleFinder, findsOneWidget);
    });
    // 4.
    testWidgets('can be checked when tapped', (tester) async {
      // TODO: Arrange
      // TODO: Act
      // TODO: Assert
    });
  });
}

var isChecked = false;
await tester.pumpWidget(
  _buildWrappedWidget(IngredientCard(
    name: mockIngredientName,
    initiallyChecked: isChecked,
    evenRow: true,
    onChecked: (newValue) {
      isChecked = newValue;
    },
  )),
);
final cardFinder = find.byType(IngredientCard);

await tester.tap(cardFinder);
await tester.pumpAndSettle();

final checkboxFinder = find.byType(Checkbox);
expect(checkboxFinder, findsOneWidget);
expect(isChecked, isTrue);

Understanding Golden Tests

While widget testing is great for checking how your widgets work, it might not cover everything about how they look. Widget tests focus more on how things function, not so much on the visual details.

AwxmiyuiwdLaxh Sarles Riyvab Iwaca = OhyyideinhDozy Buqzis Xobmud Azame = Duyr Zoojed! Wetf Kivjos! Sazyap Buybv

golden_toolkit: ^0.15.0
tags:
  golden:
flutter test --update-goldens

Writing a Golden Test

It’s time to dive into the process of creating your first golden test. IngredientCard supports the light theme very well, and going forward, it’s a good idea to test that it always stays that way.

group('Golden Tests - IngredientCard', () {
  testGoldens('can support light theme', (tester) async {
    // TODO: Arrange
    // TODO: Act
    // TODO: Assert
  });
});
import 'package:golden_toolkit/golden_toolkit.dart';
final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1)
// TODO: Scenario for Light - Unchecked
// TODO: Other scenarios
// Scenario 1
..addScenario(
  'Light - Unchecked',
  IngredientCard(
    name: mockIngredientName,
    initiallyChecked: false,
    evenRow: true,
    onChecked: (newValue) {},
  ),
)
// Scenario 2
..addScenario(
  'Light - Checked',
  IngredientCard(
    name: mockIngredientName,
    initiallyChecked: true,
    evenRow: true,
    onChecked: (newValue) {},
  ),
)
// Scenario 3
..addScenario(
  'Light - Odd - Unchecked',
  IngredientCard(
    name: mockIngredientName,
    initiallyChecked: false,
    evenRow: false,
    onChecked: (newValue) {},
  ),
)
// Scenario 4
..addScenario(
  'Light - Odd - Checked',
  IngredientCard(
    name: mockIngredientName,
    initiallyChecked: true,
    evenRow: false,
    onChecked: (newValue) {},
  ),
);
// 1.
await tester.pumpWidgetBuilder(
  // 2.
  builder.build(),
  // 3.
  wrapper: materialAppWrapper(
    theme: ThemeData.light(),
  ),
);
await screenMatchesGolden(tester, 'light_ingredient_card');

value: true,

Challenges

Challenge 1: Test IngredientCard Can Be Unchecked

You’ve already added a test to verify the opposite behavior. Use it as an example to test that IngredientCard can be unchecked when tapped and if it was initially checked when rendering the widget.

Challenge 2: Test IngredientCard Supports Dark Theme

Again, you’ve already tested that IngredientCard supports the light theme. Now it’s time to check that it also supports the dark theme.

Key Points

  • Tools like the flutter_test package provide utilities to make testing easier.
  • Widget tests are great for verifying behaviors.
  • You can verify that a widget builds correctly with a widget test.
  • WidgetTester allows you to perform multiple interactions with your widgets, like tap or even text input.
  • Golden tests are bound to a ‘golden’ image.
  • ‘Golden’ refers to the fact that you have a baseline image that represents the correct appearance of a widget under normal conditions.
  • You can catch unexpected changes to your UI with golden tests.

Where to Go From Here?

You could do so much more with widget testing, so be sure to take a look at Testing in Flutter if you want to learn about testing in much more detail.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now