flutter_test_helpers 0.0.6
flutter_test_helpers: ^0.0.6 copied to clipboard
Comprehensive Flutter testing toolkit with mock data generators, visual regression testing, performance testing, and screenshot testing utilities.
flutter_test_helpers #
A comprehensive Flutter package providing mock data generators, test utilities, and widget testing helpers to streamline your Flutter testing workflow.
Features #
- π² Mock Data Generators: Generate random test data for colors, strings, numbers, dates, and more
- π§ͺ Test Utilities: Helper functions for common testing scenarios
- π― Widget Testing Helpers: Extensions and utilities for widget testing
- π§ Extensions: Useful extensions for common Flutter types (Color, String, DateTime, List)
- πΈ Visual Regression Testing: Compare widgets with golden files for visual consistency
- β‘ Performance Testing: Measure widget performance, frame times, and memory usage
- π· Screenshot Testing: Capture and save screenshots for documentation and testing
- π Multi-Platform Support: Supports all 6 platforms (iOS, Android, Web, Windows, macOS, Linux)
- β‘ WASM Compatible: Ready for Flutter WebAssembly
- π± Flutter 3.10+: Built with the latest Flutter features
Getting Started #
Installation #
Add flutter_test_helpers
to your pubspec.yaml
:
dependencies:
flutter_test_helpers: ^0.0.6
Import #
import 'package:flutter_test_helpers/flutter_test_helpers.dart';
Usage #
Mock Data Generators #
Generate random test data for your tests:
// Generate random colors
final color = MockDataGenerators.randomColor();
final colorList = MockDataGenerators.randomColorList(5);
// Generate random strings
final string = MockDataGenerators.randomString(15);
final stringList = MockDataGenerators.randomStringList(3);
// Generate random numbers
final intValue = MockDataGenerators.randomInt(1, 100);
final doubleValue = MockDataGenerators.randomDouble(0.0, 1.0);
// Generate random dates
final date = MockDataGenerators.randomDate();
// Generate random booleans
final boolValue = MockDataGenerators.randomBool();
Test Utilities #
Create test apps and wait for conditions:
// Create a test app
final app = TestUtilities.createTestApp(MyWidget());
final themedApp = TestUtilities.createTestAppWithTheme(MyWidget(), myTheme);
// Wait for conditions
await TestUtilities.waitForCondition(() => someCondition);
// Find widgets
final finder = TestUtilities.findByKey(Key('my-key'));
final textFinder = TestUtilities.findByText('Hello World');
// Interact with widgets
await TestUtilities.tap(tester, finder);
await TestUtilities.enterText(tester, finder, 'New Text');
await TestUtilities.scrollTo(tester, finder);
Widget Testing Helpers #
Create specialized test widgets:
// Create padded widget
final paddedWidget = WidgetTestingHelpers.createPaddedWidget(
MyWidget(),
padding: EdgeInsets.all(16.0),
);
// Create constrained widget
final constrainedWidget = WidgetTestingHelpers.createConstrainedWidget(
MyWidget(),
maxWidth: 300,
maxHeight: 200,
);
// Create themed widget
final themedWidget = WidgetTestingHelpers.createThemedWidget(
MyWidget(),
theme: ThemeData(primarySwatch: Colors.blue),
);
// Create localized widget
final localizedWidget = WidgetTestingHelpers.createLocalizedWidget(
MyWidget(),
locale: Locale('en', 'US'),
);
// Create media query widget
final mediaQueryWidget = WidgetTestingHelpers.createMediaQueryWidget(
MyWidget(),
screenSize: Size(800, 600),
devicePixelRatio: 2.0,
);
Extensions #
Use helpful extensions on common Flutter types:
// Color extensions
final lighterColor = Colors.blue.lighten(0.2);
final darkerColor = Colors.blue.darken(0.2);
final contrastColor = Colors.blue.contrastColor;
final hexString = Colors.red.toHex();
// String extensions
final capitalized = 'hello'.capitalize; // 'Hello'
final titleCase = 'hello world'.titleCase; // 'Hello World'
final truncated = 'long text'.truncate(5); // 'lo...'
final isValidEmail = 'test@example.com'.isEmail; // true
// DateTime extensions
final isToday = someDate.isToday;
final startOfDay = someDate.startOfDay;
final relativeTime = someDate.relativeTime; // '2 hours ago'
// List extensions
final randomElement = [1, 2, 3, 4, 5].random;
final shuffled = [1, 2, 3, 4, 5].shuffled;
final firstOrNull = [].firstOrNull; // null
WidgetTester Extensions #
Use convenient extensions on WidgetTester:
// Tap by key, text, or type
await tester.tapByKey(Key('my-button'));
await tester.tapByText('Click me');
await tester.tapByType<ElevatedButton>();
// Enter text by key or type
await tester.enterTextByKey(Key('my-field'), 'Hello');
await tester.enterTextByType<TextField>('World');
// Scroll to widgets
await tester.scrollToKey(Key('my-widget'));
await tester.scrollToText('Hidden text');
// Wait for widgets
await tester.waitForWidget(find.byType(MyWidget));
await tester.waitForWidgetToDisappear(find.byType(LoadingWidget));
Finder Extensions #
Use helpful extensions on Finder:
// Check finder state
if (finder.hasOne) {
// Exactly one widget found
}
if (finder.hasMultiple) {
// Multiple widgets found
}
// Get widgets and elements
final widget = finder.firstWidget;
final allWidgets = finder.allWidgets;
final element = finder.firstElement;
final allElements = finder.allElements;
Visual Regression Testing #
Compare widgets with golden files for visual consistency:
// Basic visual regression test
await VisualRegressionTesting.expectWidgetMatchesGolden(
tester,
MyWidget(),
goldenName: 'my_widget_golden',
);
// With custom configuration
final config = VisualRegressionConfig(
pixelDifferenceThreshold: 0.01,
updateGoldens: false,
screenSize: Size(375, 812),
);
await VisualRegressionTesting.expectWidgetMatchesGolden(
tester,
MyWidget(),
goldenName: 'my_widget_golden',
config: config,
);
// Create visual test widgets
final testWidget = VisualRegressionTesting.createVisualTestWidget(
MyWidget(),
theme: ThemeData(useMaterial3: true),
);
// Multi-theme testing
final multiThemeWidget = VisualRegressionTesting.createMultiThemeTestWidget(
MyWidget(),
themes: [lightTheme, darkTheme],
);
// Responsive testing
final responsiveWidget = VisualRegressionTesting.createResponsiveTestWidget(
MyWidget(),
screenSizes: [Size(375, 812), Size(768, 1024)],
);
Performance Testing #
Measure widget performance, frame times, and memory usage:
// Measure widget performance
final metrics = await PerformanceTesting.measureWidgetPerformance(
tester,
MyWidget(),
);
print('FPS: ${metrics.fps}');
print('Average frame time: ${metrics.averageFrameTimeMs}ms');
print('Meets requirements: ${metrics.meetsRequirements}');
// Measure action performance
final actionMetrics = await PerformanceTesting.measureActionPerformance(
tester,
() async {
await tester.tap(find.byKey(Key('button')));
await tester.pumpAndSettle();
},
);
// Measure scroll performance
final scrollMetrics = await PerformanceTesting.measureScrollPerformance(
tester,
SingleChildScrollView(child: MyWidget()),
scrollDistance: 1000.0,
);
// Measure animation performance
final animationMetrics = await PerformanceTesting.measureAnimationPerformance(
tester,
AnimatedContainer(
duration: Duration(seconds: 1),
child: MyWidget(),
),
);
// Custom performance configuration
final config = PerformanceConfig(
maxFrameTimeMs: 16.67, // 60 FPS
maxFrames: 100,
warmUpDuration: Duration(seconds: 1),
measurementDuration: Duration(seconds: 3),
trackMemory: true,
);
final metrics = await PerformanceTesting.measureWidgetPerformance(
tester,
MyWidget(),
config: config,
);
Screenshot Testing #
Capture and save screenshots for documentation and testing:
// Take screenshot of widget
final screenshot = await ScreenshotTesting.takeScreenshot(
tester,
MyWidget(),
);
// Take screenshot and save to file
final file = await ScreenshotTesting.takeScreenshotAndSave(
tester,
MyWidget(),
filename: 'my_widget_screenshot',
);
// Take current screenshot
final currentScreenshot = await ScreenshotTesting.takeCurrentScreenshot(tester);
// Responsive screenshots
final files = await ScreenshotTesting.takeResponsiveScreenshots(
tester,
MyWidget(),
screenSizes: [
Size(375, 812), // iPhone
Size(768, 1024), // iPad
Size(1920, 1080), // Desktop
],
baseFilename: 'my_widget_responsive',
);
// Themed screenshots
final themedFiles = await ScreenshotTesting.takeThemedScreenshots(
tester,
MyWidget(),
themes: [lightTheme, darkTheme],
baseFilename: 'my_widget_themed',
);
// Device frame screenshots
final deviceFrameWidget = ScreenshotTesting.createDeviceFrameWidget(
MyWidget(),
deviceType: DeviceType.iphone,
);
// Custom screenshot configuration
final config = ScreenshotConfig(
screenshotDirectory: 'test/screenshots',
imageFormat: ImageFormat.png,
devicePixelRatio: 3.0,
screenSize: Size(375, 812),
includeDeviceFrame: true,
backgroundColor: Colors.white,
waitForAnimations: true,
);
Creating Extension Methods #
To use the testing utilities with convenient extension methods on WidgetTester
, create a file in your test directory:
// test/test_extensions.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_test_helpers/flutter_test_helpers.dart';
/// Extension methods for easier performance testing
extension PerformanceTestingExtension on WidgetTester {
Future<PerformanceMetrics> measureWidgetPerformance(
Widget widget, {
PerformanceConfig? config,
}) async {
return PerformanceTesting.measureWidgetPerformance(this, widget, config: config);
}
}
/// Extension methods for easier screenshot testing
extension ScreenshotTestingExtension on WidgetTester {
Future<Uint8List> takeScreenshot(Widget widget, {ScreenshotConfig? config}) async {
return ScreenshotTesting.takeScreenshot(this, widget, config: config);
}
}
/// Extension methods for easier visual regression testing
extension VisualRegressionTestingExtension on WidgetTester {
Future<void> expectWidgetMatchesGolden(
Widget widget, {
required String goldenName,
VisualRegressionConfig? config,
}) async {
await VisualRegressionTesting.expectWidgetMatchesGolden(
this,
widget,
goldenName: goldenName,
config: config,
);
}
}
Then import this file in your test files:
import 'test_extensions.dart';
// Now you can use extension methods
await tester.measureWidgetPerformance(MyWidget());
await tester.takeScreenshot(MyWidget());
await tester.expectWidgetMatchesGolden(MyWidget(), goldenName: 'my_widget');
Example Test #
Here's a complete example of how to use flutter_test_helpers
in your tests:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_test_helpers/flutter_test_helpers.dart';
void main() {
group('MyWidget Tests', () {
testWidgets('should display and handle interactions', (WidgetTester tester) async {
// Create test app with helpers
final app = TestUtilities.createTestApp(
WidgetTestingHelpers.createPaddedWidget(
MyWidget(
title: MockDataGenerators.randomString(10),
color: MockDataGenerators.randomColor(),
),
),
);
await tester.pumpWidget(app);
// Use helper methods to find and interact
final titleFinder = TestUtilities.findByText('My Widget');
expect(titleFinder, findsOneWidget);
// Use extensions for easier interaction
await tester.tapByKey(Key('action-button'));
await tester.pumpAndSettle();
// Wait for state changes
await tester.waitForWidget(find.byType(SuccessWidget));
});
testWidgets('should pass visual regression test', (WidgetTester tester) async {
final widget = MyWidget(
title: 'Test Title',
color: Colors.blue,
);
// Visual regression test
await VisualRegressionTesting.expectWidgetMatchesGolden(
tester,
widget,
goldenName: 'my_widget_golden',
);
});
testWidgets('should meet performance requirements', (WidgetTester tester) async {
final widget = MyWidget(
title: 'Performance Test',
color: Colors.red,
);
// Performance test
final metrics = await PerformanceTesting.measureWidgetPerformance(tester, widget);
expect(metrics.meetsRequirements, isTrue);
expect(metrics.fps, greaterThan(30.0));
});
testWidgets('should capture screenshots', (WidgetTester tester) async {
final widget = MyWidget(
title: 'Screenshot Test',
color: Colors.green,
);
// Screenshot test
final file = await ScreenshotTesting.takeScreenshotAndSave(
tester,
widget,
filename: 'my_widget_screenshot',
);
expect(file.existsSync(), isTrue);
});
});
}
Platform Support #
This package supports all Flutter platforms:
- β iOS - Full support
- β Android - Full support
- β Web - Full support
- β Windows - Full support
- β macOS - Full support
- β Linux - Full support
- β WASM - Compatible with Flutter WebAssembly
Requirements #
- Flutter: >=3.10.0
- Dart: >=3.0.0
- Flutter Test: Included in Flutter SDK
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Author #
Dhia Bechattaoui
- GitHub: @Dhia-Bechattaoui
Support #
If you encounter any issues or have questions, please:
- Check the documentation
- Search existing issues
- Create a new issue
Made with β€οΈ for the Flutter community