Flutter测试辅助插件surf_widget_test_composer的使用
Flutter测试辅助插件 surf_widget_test_composer
的使用
surf_widget_test_composer
是一个由 Surf 团队开发的实用工具包,旨在简化 Flutter 应用中的小部件和黄金测试(Golden Tests)过程。它基于 golden_toolkit
包构建,提供了丰富的功能来促进高效的测试工作流程。
概述
该插件可以帮助开发者通过配置文件轻松设置本地化、主题、设备等参数,并自动生成多种场景下的黄金图像(Goldens),从而简化了 UI 测试的过程。
安装
在你的 pubspec.yaml
文件中添加 surf_widget_test_composer
依赖:
dependencies:
surf_widget_test_composer: $currentVersion$
请确保将 $currentVersion$
替换为最新版本号。
示例 Demo
初始化配置
首先,你需要创建一个名为 test/flutter_test_config.dart
的文件,在其中指定应用的本地化、主题、测试设备列表以及黄金测试的容差值。
假设你有两个主题(浅色和深色)、三种设备(iPhone 11、Google Pixel 4a 和 iPhone SE 1)、两种语言(英语和俄语)。你的 flutter_test_config.dart
文件可能如下所示:
import 'package:surf_widget_test_composer/surf_widget_test_composer.dart' as helper;
/// Localization and locales from auto-generated AppLocalizations.
const _localizations = AppLocalizations.localizationsDelegates;
const _locales = AppLocalizations.supportedLocales;
Future<void> testExecutable(FutureOr<void> Function() testMain) {
/// Define themes for testing.
final themes = [
helper.TestingTheme(
data: ThemeData.dark(),
stringified: 'dark',
type: helper.ThemeType.dark,
),
helper.TestingTheme(
data: ThemeData.light(),
stringified: 'light',
type: helper.ThemeType.light,
),
];
/// Define devices for testing.
final devices = [
helper.TestDevice(
name: 'iphone11',
size: const Size(414, 896),
safeArea: const EdgeInsets.only(top: 44, bottom: 34),
),
helper.TestDevice(
name: 'pixel 4a',
size: const Size(393, 851),
),
helper.TestDevice(
name: 'iphone_se_1',
size: const Size(640 / 2, 1136 / 2),
),
];
return helper.testExecutable(
testMain: testMain,
themes: themes,
localizations: _localizations,
locales: _locales,
wrapper: (child, mode, theme, localizations, locales) =>
helper.BaseWidgetTestWrapper(
childBuilder: child,
mode: mode,
themeData: theme,
localizations: localizations,
localeOverrides: locales,
dependencies: (child) => child,
),
backgroundColor: (theme) => theme.colorScheme.background,
devicesForTest: devices,
tolerance: 0.5,
);
}
根据上述配置,每个测试将会生成 12 张黄金图片:2 种语言 × 2 种主题 × 3 台设备。
使用示例
基本使用
如果你需要进行除了黄金测试之外的小部件测试,可以参考以下代码:
class MockSettingsService extends Mock implements SettingsService {}
void main() {
final mockSettingsService = MockSettingsService();
const widget = SettingsScreen();
/// Generate golden.
testWidget<SettingsScreen>(
desc: 'SettingsScreen',
widgetBuilder: (context, theme) => ProviderScope(
overrides: [
settingsServiceProvider.overrideWithValue(mockSettingsService),
],
child: Consumer(
builder: (context, ref, _) => widget.build(context, ref),
),
),
setup: (context, mode) {
registerFallbackValue(ThemeMode.light);
when(() => mockSettingsService.themeMode()).thenAnswer(
(_) => Future.value(ThemeMode.dark),
);
when(() => mockSettingsService.updateThemeMode(any()))
.thenAnswer((_) => Future.value());
},
test: (tester, context) async {
final button = find.byType(DropdownButton<ThemeMode>);
expect(button, findsOneWidget);
final floatingActionButton = find.byIcon(Icons.light_mode);
expect(floatingActionButton, findsOneWidget);
verifyNever(() => mockSettingsService.updateThemeMode(any()));
await tester.tap(floatingActionButton);
verify(() => mockSettingsService.updateThemeMode(any())).called(1);
await tester.pumpAndSettle();
expect(find.byIcon(Icons.mode_night), findsOneWidget);
},
);
}
纯黄金测试
如果你只需要生成黄金图片而不需要执行其他测试,可以这样写:
void main() {
const widget = SampleItemListView();
/// Generate golden.
testWidget<SampleItemListView>(
desc: 'SampleItemListView - result',
widgetBuilder: (context, _) => widget.build(context),
screenState: 'result', // Optional: specify the state of the widget
);
}
Riverpod 示例
对于使用 Riverpod 状态管理的应用,你可以这样编写测试:
class MockRiverpodCounterScreenController extends AutoDisposeNotifier<int>
with Mock
implements RiverpodCounterScreenController {}
void main() {
const int testValue = 5;
const widget = RiverpodCounterScreen();
final mockController = MockRiverpodCounterScreenController();
final container = ProviderContainer(
overrides: [
riverpodCounterScreenControllerProvider.overrideWith(() => mockController),
],
);
/// Generate golden.
testWidget<RiverpodCounterScreen>(
desc: 'RiverpodCounterScreen',
widgetBuilder: (context, theme) => UncontrolledProviderScope(
container: container,
child: Consumer(
builder: (context, ref, _) => widget.build(context, ref),
),
),
setup: (context, mode) {
when(() => mockController.build()).thenReturn(testValue);
when(() => mockController.increment()).thenReturn(null);
},
test: (tester, context) async {
expect(find.widgetWithText(Center, testValue.toString()), findsOneWidget);
final floatingActionButton = find.byIcon(Icons.add);
expect(floatingActionButton, findsOneWidget);
await tester.tap(floatingActionButton);
verify(() => mockController.increment()).called(1);
},
);
}
生成黄金图片
在运行测试之前,记得更新黄金图片:
flutter test --update-goldens --tags=golden
注意事项
-
在测试时,如果遇到动画导致的无限循环问题,可以通过定义自定义泵函数或在特定环境下调整动画行为来解决。
-
总是明确指定测试小部件的泛型类型(例如
testWidget<TestableScreen>
),因为黄金图片的命名是基于小部件类名生成的。
以上就是关于 surf_widget_test_composer
插件的基本使用方法及其示例。希望这些信息能帮助你更好地进行 Flutter 应用的测试!
更多关于Flutter测试辅助插件surf_widget_test_composer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter测试辅助插件surf_widget_test_composer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用surf_widget_test_composer
插件进行UI测试的示例代码。surf_widget_test_composer
是一个强大的Flutter测试辅助插件,它允许开发者以交互方式构建和测试他们的UI组件。
首先,确保你已经将surf_widget_test_composer
添加到你的pubspec.yaml
文件中:
dependencies:
flutter:
sdk: flutter
surf_widget_test_composer: ^x.y.z # 替换为最新版本号
然后,运行flutter pub get
来安装依赖。
使用surf_widget_test_composer
进行测试
以下是一个简单的Flutter Widget,以及如何使用surf_widget_test_composer
来测试它的示例。
1. 创建一个简单的Widget
// my_widget.dart
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Widget'),
),
body: Center(
child: Text('Hello, World!'),
),
);
}
}
2. 编写测试代码
在你的test
文件夹中,创建一个新的测试文件,例如my_widget_test.dart
。
// my_widget_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:surf_widget_test_composer/surf_widget_test_composer.dart';
import 'package:your_app/my_widget.dart'; // 替换为你的实际路径
void main() {
testWidgets('test MyWidget using surf_widget_test_composer', (WidgetTester tester) async {
// 初始化Composer
final composer = await SurfComposer.init(
tester: tester,
initialWidget: MaterialApp(home: MyWidget()),
);
// 查找AppBar的标题并验证其内容
await composer
.findByText('My Widget', matcher: containsText('My Widget'))
.verify();
// 查找Center中的Text并验证其内容
await composer
.findByText('Hello, World!', matcher: containsText('Hello, World!'))
.verify();
// 你可以添加更多的交互和验证步骤,例如点击、滑动等
// await composer.findByType(ElevatedButton).tap(); // 假设有一个按钮
// 关闭Composer
await composer.close();
});
}
解释
-
初始化Composer:
SurfComposer.init
方法用于初始化Composer并设置初始Widget。在这个例子中,我们设置了一个包含MyWidget
的MaterialApp
。 -
查找和验证:使用
composer.findByText
方法查找包含特定文本的Widget,并使用verify
方法验证其存在和内容。 -
交互:你可以使用
tap
、scroll
等方法来模拟用户交互。在上面的例子中,我们注释掉了一个假设的按钮点击操作。 -
关闭Composer:完成测试后,调用
composer.close()
方法关闭Composer。
这个示例展示了如何使用surf_widget_test_composer
来查找和验证Widget的存在和内容。你可以根据需要添加更多的交互和验证步骤来构建更复杂的测试。