flutter-testing by madteacher/mad-agents-skills
npx skills add https://github.com/madteacher/mad-agents-skills --skill flutter-testing此技能为测试 Flutter 应用程序提供了全面的指导,涵盖所有测试类型。Flutter 测试分为三类:
一个经过良好测试的 Flutter 应用应包含许多用于代码覆盖率的单元和 Widget 测试,以及足够覆盖重要使用场景的集成测试。
| 权衡点 | 单元测试 | Widget 测试 | 集成测试 |
|---|---|---|---|
| 置信度 | 低 | 较高 | 最高 |
| 维护成本 | 低 | 较高 | 最高 |
| 依赖项 | 少 | 较多 | 最多 |
| 执行速度 | 快 | 快 | 慢 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
Flutter 支持三种构建模式,对测试有不同的影响:
单元测试测试单个函数、方法或类。模拟外部依赖,避免磁盘 I/O 或 UI 渲染。
import 'package:test/test.dart';
import 'package:my_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
运行命令:flutter test
Widget 测试测试单个 Widget,以验证 UI 外观和交互。
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('MyWidget has a title and message', (tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
final titleFinder = find.text('T');
final messageFinder = find.text('M');
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
集成测试在真实设备或模拟器上测试完整的应用程序。
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('tap button, verify counter', (tester) async {
await tester.pumpWidget(const MyApp());
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey('increment')));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});
}
运行命令:flutter test integration_test/
单元测试验证在各种条件下逻辑单元的正确性。
package:test/test.dart关于模拟依赖项、插件交互和复杂场景,请参阅 单元测试参考。
Widget 测试在测试环境中验证 Widget 的 UI 外观和行为。
// 通过文本
final titleFinder = find.text('Title');
// 通过 Widget 类型
final buttonFinder = find.byType(ElevatedButton);
// 通过 key
final fabFinder = find.byKey(const ValueKey('increment'));
// 通过 Widget 实例
final myWidgetFinder = find.byWidget(myWidgetInstance);
// 点击
await tester.tap(buttonFinder);
// 拖动
await tester.drag(listFinder, const Offset(0, -300));
// 输入文本
await tester.enterText(fieldFinder, 'Hello World');
// 滚动
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
testWidgets('widget in landscape mode', (tester) async {
// 设置为横屏
await tester.binding.setSurfaceSize(const Size(800, 400));
await tester.pumpWidget(const MyApp());
// 验证横屏行为
expect(find.byType(MyWidget), findsOneWidget);
// 重置为竖屏
addTearDown(tester.binding.setSurfaceSize(null));
});
关于滚动、复杂交互和性能测试,请参阅 Widget 测试参考。
集成测试在真实设备或模拟器上测试完整的应用程序或大部分功能。
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end-to-end test', () {
testWidgets('complete user flow', (tester) async {
await tester.pumpWidget(const MyApp());
// 步骤 1: 导航到屏幕
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
// 步骤 2: 输入凭据
await tester.enterText(find.byKey(const Key('username')), 'user');
await tester.enterText(find.byKey(const Key('password')), 'pass');
// 步骤 3: 提交
await tester.tap(find.text('Submit'));
await tester.pumpAndSettle();
// 验证结果
expect(find.text('Welcome'), findsOneWidget);
});
});
}
testWidgets('scrolling performance', (tester) async {
await tester.pumpWidget(const MyApp());
final listFinder = find.byType(ListView);
// 测量性能
final timeline = await tester.trace(() async {
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
});
// 分析时间线数据
expect(timeline.frames.length, greaterThan(10));
});
关于性能分析、CI 集成和复杂场景,请参阅 集成测试参考。
当测试使用插件的代码时,需要特殊处理以避免崩溃。
如果你的 Flutter 应用使用了插件,你需要在单元/Widget 测试中模拟平台通道调用。
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
// 模拟平台通道
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('your.plugin.channel'),
(MethodCall methodCall) async {
if (methodCall.method == 'getPlatformVersion') {
return 'Android 12';
}
return null;
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('your.plugin.channel'),
null,
);
});
}
关于测试 Flutter 插件(包括原生代码)的全面指导,请参阅 插件测试参考。
黄色和黑色条纹表示溢出。通常由 Row/Column 中的无约束子 Widget 引起。
解决方案: 将溢出的 Widget 包裹在 Expanded 或 Flexible 中。
// 问题
Row(
children: [
Icon(Icons.message),
Column(children: [Text('Very long text...')]), // 溢出!
],
)
// 解决方案
Row(
children: [
Icon(Icons.message),
Expanded(child: Column(children: [Text('Very long text...')])),
],
)
当 ListView(或其他可滚动 Widget)在没有高度约束的 Column 内部时发生。
解决方案: 用 Expanded 包裹或使用 shrinkWrap: true。
// 问题
Column(
children: [
Text('Header'),
ListView(children: [...]), // 错误!
],
)
// 解决方案
Column(
children: [
Text('Header'),
Expanded(child: ListView(children: [...])),
],
)
切勿在 build 方法中调用 setState。
解决方案: 使用 Navigator API 或推迟到构建后回调中执行。
更多错误和解决方案,请参阅 常见错误参考。
flutter test
flutter test test/widget_test.dart
flutter test integration_test/
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
# Android
flutter test --platform android
# iOS
flutter test --platform ios
# Web
flutter test --platform chrome
flutter test --no-sound-null-safety test/my_test.dart
flutter test --verbose
flutter test --name "Counter value should be incremented"
每周安装量
881
仓库
GitHub Stars
87
首次出现
Jan 22, 2026
安全审计
安装于
codex731
opencode711
gemini-cli700
github-copilot663
kimi-cli600
amp591
This skill provides comprehensive guidance for testing Flutter applications across all test types. Flutter testing falls into three categories:
A well-tested Flutter app has many unit and widget tests for code coverage, plus enough integration tests to cover important use cases.
| Tradeoff | Unit | Widget | Integration |
|---|---|---|---|
| Confidence | Low | Higher | Highest |
| Maintenance cost | Low | Higher | Highest |
| Dependencies | Few | More | Most |
| Execution speed | Quick | Quick | Slow |
Flutter supports three build modes with different implications for testing:
Unit tests test a single function, method, or class. Mock external dependencies and avoid disk I/O or UI rendering.
import 'package:test/test.dart';
import 'package:my_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
Run with: flutter test
Widget tests test single widgets to verify UI appearance and interaction.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('MyWidget has a title and message', (tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
final titleFinder = find.text('T');
final messageFinder = find.text('M');
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
Integration tests test complete apps on real devices or emulators.
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('tap button, verify counter', (tester) async {
await tester.pumpWidget(const MyApp());
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey('increment')));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});
}
Run with: flutter test integration_test/
What are you testing?
Does it depend on plugins/native code?
Need to mock dependencies?
Encountering errors?
Unit tests verify the correctness of a unit of logic under various conditions.
package:test/test.dartFor mocking dependencies, plugin interactions, and complex scenarios, see Unit Testing Reference.
Widget tests verify widget UI appearance and behavior in a test environment.
// By text
final titleFinder = find.text('Title');
// By widget type
final buttonFinder = find.byType(ElevatedButton);
// By key
final fabFinder = find.byKey(const ValueKey('increment'));
// By widget instance
final myWidgetFinder = find.byWidget(myWidgetInstance);
// Tap
await tester.tap(buttonFinder);
// Drag
await tester.drag(listFinder, const Offset(0, -300));
// Enter text
await tester.enterText(fieldFinder, 'Hello World');
// Scroll
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
testWidgets('widget in landscape mode', (tester) async {
// Set to landscape
await tester.binding.setSurfaceSize(const Size(800, 400));
await tester.pumpWidget(const MyApp());
// Verify landscape behavior
expect(find.byType(MyWidget), findsOneWidget);
// Reset to portrait
addTearDown(tester.binding.setSurfaceSize(null));
});
For scrolling, complex interactions, and performance testing, see Widget Testing Reference.
Integration tests test complete apps or large parts on real devices or emulators.
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end-to-end test', () {
testWidgets('complete user flow', (tester) async {
await tester.pumpWidget(const MyApp());
// Step 1: Navigate to screen
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
// Step 2: Enter credentials
await tester.enterText(find.byKey(const Key('username')), 'user');
await tester.enterText(find.byKey(const Key('password')), 'pass');
// Step 3: Submit
await tester.tap(find.text('Submit'));
await tester.pumpAndSettle();
// Verify result
expect(find.text('Welcome'), findsOneWidget);
});
});
}
testWidgets('scrolling performance', (tester) async {
await tester.pumpWidget(const MyApp());
final listFinder = find.byType(ListView);
// Measure performance
final timeline = await tester.trace(() async {
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
});
// Analyze timeline data
expect(timeline.frames.length, greaterThan(10));
});
For performance profiling, CI integration, and complex scenarios, see Integration Testing Reference.
When testing code that uses plugins, special handling is required to avoid crashes.
If your Flutter app uses plugins, you need to mock the platform channel calls in unit/widget tests.
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
// Mock platform channel
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('your.plugin.channel'),
(MethodCall methodCall) async {
if (methodCall.method == 'getPlatformVersion') {
return 'Android 12';
}
return null;
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('your.plugin.channel'),
null,
);
});
}
For comprehensive guidance on testing Flutter plugins (including native code), see Plugin Testing Reference.
Yellow and black stripes indicate overflow. Usually caused by unconstrained children in Row/Column.
Solution: Wrap the overflowing widget in Expanded or Flexible.
// Problem
Row(
children: [
Icon(Icons.message),
Column(children: [Text('Very long text...')]), // Overflow!
],
)
// Solution
Row(
children: [
Icon(Icons.message),
Expanded(child: Column(children: [Text('Very long text...')])),
],
)
Occurs when ListView (or other scrollable) is inside Column without height constraints.
Solution: Wrap in Expanded or use shrinkWrap: true.
// Problem
Column(
children: [
Text('Header'),
ListView(children: [...]), // Error!
],
)
// Solution
Column(
children: [
Text('Header'),
Expanded(child: ListView(children: [...])),
],
)
Never call setState during build method.
Solution: Use Navigator API or defer to post-build callback.
For more errors and solutions, see Common Errors Reference.
flutter test
flutter test test/widget_test.dart
flutter test integration_test/
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
# Android
flutter test --platform android
# iOS
flutter test --platform ios
# Web
flutter test --platform chrome
flutter test --no-sound-null-safety test/my_test.dart
flutter test --verbose
flutter test --name "Counter value should be incremented"
Weekly Installs
881
Repository
GitHub Stars
87
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex731
opencode711
gemini-cli700
github-copilot663
kimi-cli600
amp591
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Gemini Interactions API 指南:统一接口、智能体交互与服务器端状态管理
833 周安装
Apollo MCP 服务器:让AI代理通过GraphQL API交互的完整指南
834 周安装
智能体记忆系统构建指南:分块策略、向量存储与检索优化
835 周安装
Scrapling官方网络爬虫框架 - 自适应解析、绕过Cloudflare、Python爬虫库
836 周安装
抽奖赢家选取器 - 随机选择工具,支持CSV、Excel、Google Sheets,公平透明
838 周安装
Medusa 前端开发指南:使用 SDK、React Query 构建电商商店
839 周安装