In software development, UI testing ensures that the user interface behaves and renders correctly. Flutter, Google’s UI toolkit, offers a rich set of testing tools. Among these tools, golden tests (also known as snapshot tests) are invaluable for verifying the visual aspects of your application. This post will guide you through the process of using golden tests for UI testing in Flutter.
What are Golden Tests?
Golden tests, or snapshot tests, capture a ‘golden’ image or output of a UI component. Subsequent test runs compare the current output against this golden image. If there’s a mismatch, the test fails, indicating a visual change that needs review. These tests are crucial for catching unintended visual regressions.
Why Use Golden Tests?
- Visual Regression Detection: Quickly identify unintended changes to the UI.
- Consistency: Ensure that UI elements look the same across different devices and platforms.
- Efficiency: Test complex UI components with minimal code.
- Confidence: Gain confidence that UI updates don’t introduce visual bugs.
Setting Up Golden Tests in Flutter
Step 1: Add Dependencies
First, you need to add the necessary dependencies to your pubspec.yaml
file:
dev_dependencies:
flutter_test:
sdk: flutter
golden_toolkit: ^0.16.0 # Use the latest version
flutter:
generate: true
After adding the dependencies, run flutter pub get
to install them.
Step 2: Configure Golden Test Assets
Make sure you have enabled the generation of generated plugins. Usually it can be achieved adding
flutter:
to your pubspec.yaml file. If that’s not the case, add the generation step manually (e.g.
generate: true
when you don’t use plugins):
flutter gen-l10n
Step 3: Create Your First Golden Test
Now, let’s create a simple UI component and a golden test for it.
Example UI Component
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.blue,
child: const Text(
'Hello, Golden Test!',
style: TextStyle(color: Colors.white),
),
);
}
}
Golden Test Implementation
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:your_app_name/my_widget.dart'; // Replace with your actual path
void main() {
testGoldens('MyWidget should look correct', (tester) async {
await loadAppFonts();
final builder = DeviceBuilder()
..overrideDevices((DeviceBuilderCallback deviceBuilder) => [
deviceBuilder.device.copyWith(name: 'phone', size: Size(400, 200))
])
..addScenario(
widget: MyWidget(),
name: 'default',
);
await tester.pumpDeviceBuilder(builder);
await screenMatchesGolden(tester, 'my_widget');
});
}
In this example:
- We import the necessary packages, including
flutter_test
andgolden_toolkit
. - The
testGoldens
function is used to define the golden test. loadAppFonts()
ensures that fonts are loaded before the test runs.DeviceBuilder
helps define different device screen sizes to run the golden test against.- We create an instance of
MyWidget
and pass it to theDeviceBuilder
‘s addScenario
method. screenMatchesGolden
compares the rendered widget against the golden image namedmy_widget
.
Step 4: Run the Golden Test
To run the golden test, use the following command:
flutter test --update-goldens
The --update-goldens
flag tells Flutter to generate or update the golden images. The first time you run this command, it will create the golden image in the test/goldens
directory.
Step 5: Verify and Maintain Golden Tests
When the test fails, it means the current UI doesn’t match the golden image. Review the changes carefully to determine if the mismatch is intentional or due to a bug.
- Intentional Changes: If the changes are intentional, update the golden image by running
flutter test --update-goldens
. - Unintentional Changes: If the changes are unintentional, investigate and fix the bug.
Advanced Golden Testing Techniques
1. Testing Different Themes and Locales
Golden tests can be extended to test UI components with different themes and locales.
testGoldens('MyWidget with dark theme should look correct', (tester) async {
await loadAppFonts();
final builder = DeviceBuilder()
..overrideDevices((DeviceBuilderCallback deviceBuilder) => [
deviceBuilder.device.copyWith(name: 'phone', size: Size(400, 200))
])
..addScenario(
widget: MaterialApp(
theme: ThemeData.dark(),
home: MyWidget(),
),
name: 'dark_theme',
);
await tester.pumpDeviceBuilder(builder);
await screenMatchesGolden(tester, 'my_widget_dark');
});
2. Testing on Different Devices
You can define multiple device configurations using the golden_toolkit
‘s DeviceBuilder
. It provides a device definition with predefined device types.
import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testGoldens('MyWidget should look correct on multiple devices', (tester) async {
await loadAppFonts();
final builder = DeviceBuilder()
..addScenario(
widget: MyWidget(),
name: 'default',
);
await tester.pumpDeviceBuilder(builder);
await screenMatchesGolden(tester, 'my_widget_multi_device');
});
}
3. Handling Dynamic Data
When dealing with dynamic data, ensure that your tests are stable by mocking or stubbing the data sources. Use predictable, consistent data for your golden tests.
class MockDataProvider {
String getData() {
return 'Consistent Data';
}
}
class MyWidgetWithData extends StatelessWidget {
final MockDataProvider dataProvider;
MyWidgetWithData(this.dataProvider);
@override
Widget build(BuildContext context) {
return Text(dataProvider.getData());
}
}
void main() {
testGoldens('MyWidgetWithData should look correct with mocked data', (tester) async {
await loadAppFonts();
final builder = DeviceBuilder()
..overrideDevices((DeviceBuilderCallback deviceBuilder) => [
deviceBuilder.device.copyWith(name: 'phone', size: Size(400, 200))
])
..addScenario(
widget: MyWidgetWithData(MockDataProvider()),
name: 'mocked_data',
);
await tester.pumpDeviceBuilder(builder);
await screenMatchesGolden(tester, 'my_widget_with_data');
});
}
Best Practices for Golden Tests
- Keep Tests Focused: Each test should focus on a single UI component or interaction.
- Automate Updates: Integrate golden test updates into your CI/CD pipeline.
- Review Changes: Always review visual changes when a golden test fails.
- Use Version Control: Store golden images in your version control system to track changes.
- Descriptive Names: Use descriptive names for your golden images to make it easier to understand what each test verifies.
Conclusion
Golden tests are a powerful tool for UI testing in Flutter, providing a reliable way to detect visual regressions and ensure consistency. By setting up and maintaining golden tests, you can increase the confidence in your UI and deliver a high-quality user experience. With the golden_toolkit
package, creating and managing golden tests becomes straightforward, allowing you to focus on building great Flutter applications.