flutter-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlutter Testing
Flutter 测试
Overview
概述
This skill provides comprehensive guidance for testing Flutter applications across all test types. Flutter testing falls into three categories:
- Unit tests - Test individual functions, methods, or classes in isolation
- Widget tests (component tests) - Test single widgets and verify UI appearance and behavior
- Integration tests - Test complete apps or large parts to verify end-to-end functionality
A well-tested Flutter app has many unit and widget tests for code coverage, plus enough integration tests to cover important use cases.
本指南为Flutter应用的各类测试提供全面指导。Flutter测试分为三类:
- Unit tests - 独立测试单个函数、方法或类
- Widget tests(组件测试)- 测试单个Widget并验证UI外观与行为
- Integration tests - 测试完整应用或大型模块以验证端到端功能
一个测试完善的Flutter应用会包含大量单元测试和Widget测试以保证代码覆盖率,同时搭配足够的集成测试覆盖核心用户场景。
Test Type Trade-offs
测试类型权衡
| Tradeoff | Unit | Widget | Integration |
|---|---|---|---|
| Confidence | Low | Higher | Highest |
| Maintenance cost | Low | Higher | Highest |
| Dependencies | Few | More | Most |
| Execution speed | Quick | Quick | Slow |
| 权衡维度 | Unit测试 | Widget测试 | Integration测试 |
|---|---|---|---|
| 测试可信度 | 低 | 较高 | 最高 |
| 维护成本 | 低 | 较高 | 最高 |
| 依赖项数量 | 少 | 较多 | 最多 |
| 执行速度 | 快速 | 快速 | 缓慢 |
Build Modes for Testing
测试用构建模式
Flutter supports three build modes with different implications for testing:
- Debug mode - Use during development with hot reload. Assertions enabled, debugging enabled, but performance is janky
- Profile mode - Use for performance analysis. Similar to release mode but with some debugging features enabled
- Release mode - Use for deployment. Assertions disabled, optimized for speed and size
Flutter支持三种构建模式,各模式对测试的影响不同:
- Debug mode - 开发阶段使用,支持热重载。启用断言和调试功能,但性能表现一般
- Profile mode - 用于性能分析。与Release模式类似,但保留部分调试功能
- Release mode - 用于部署。禁用断言,针对速度和体积进行优化
Quick Start
快速入门
Unit Tests
Unit Tests
Unit tests test a single function, method, or class. Mock external dependencies and avoid disk I/O or UI rendering.
dart
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 testUnit tests用于测试单个函数、方法或类。模拟外部依赖项,避免磁盘I/O或UI渲染。
dart
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 testWidget Tests
Widget Tests
Widget tests test single widgets to verify UI appearance and interaction.
dart
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);
});
}Widget Tests用于测试单个Widget,验证其UI外观与交互逻辑。
dart
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
Integration Tests
Integration tests test complete apps on real devices or emulators.
dart
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/Integration Tests用于在真实设备或模拟器上测试完整应用。
dart
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/Testing Workflow Decision Tree
测试工作流决策树
-
What are you testing?
- Single function/class → Unit Tests
- Single widget/component → Widget Tests
- Complete user flow → Integration Tests
-
Does it depend on plugins/native code?
- Yes → See Plugins in Tests or Testing Plugins
-
Need to mock dependencies?
- Yes → See Mocking Guide
-
Encountering errors?
- See Common Testing Errors
-
你要测试什么?
- 单个函数/类 → Unit Tests
- 单个Widget/组件 → Widget Tests
- 完整用户流程 → Integration Tests
-
是否依赖插件/原生代码?
-
是否需要模拟依赖项?
- 是 → 查看模拟指南
-
遇到错误?
- 查看常见测试错误
Unit Tests
Unit Tests
Unit tests verify the correctness of a unit of logic under various conditions.
Unit Tests用于验证逻辑单元在不同场景下的正确性。
When to Use Unit Tests
适用场景
- Testing business logic functions
- Validating data transformations
- Testing state management logic
- Mocking external services/API calls
- 测试业务逻辑函数
- 验证数据转换逻辑
- 测试状态管理逻辑
- 模拟外部服务/API调用
Key Concepts
核心概念
- Use
package:test/test.dart - Mock dependencies using Mockito or similar
- Avoid file I/O or UI rendering
- Fast execution, high maintainability
- 使用包
package:test/test.dart - 使用Mockito等工具模拟依赖项
- 避免文件I/O或UI渲染
- 执行速度快,维护成本低
Advanced Unit Testing
进阶Unit测试
For mocking dependencies, plugin interactions, and complex scenarios, see Unit Testing Reference.
关于依赖项模拟、插件交互及复杂场景测试,查看Unit测试参考。
Widget Tests
Widget Tests
Widget tests verify widget UI appearance and behavior in a test environment.
Widget Tests用于在测试环境中验证Widget的UI外观与行为。
When to Use Widget Tests
适用场景
- Testing widget rendering
- Verifying user interactions (taps, drags, scrolling)
- Testing different orientations
- Validating widget state changes
- 测试Widget渲染效果
- 验证用户交互(点击、拖拽、滚动)
- 测试不同屏幕方向
- 验证Widget状态变化
Widget Testing Patterns
Widget测试模式
Finding Widgets
查找Widget
dart
// 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);dart
// 通过文本查找
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);User Interactions
用户交互模拟
dart
// 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();dart
// 点击
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();Testing Different Orientations
测试不同屏幕方向
dart
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));
});dart
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));
});Advanced Widget Testing
进阶Widget测试
For scrolling, complex interactions, and performance testing, see Widget Testing Reference.
关于滚动测试、复杂交互及性能测试,查看Widget测试参考。
Integration Tests
Integration Tests
Integration tests test complete apps or large parts on real devices or emulators.
Integration Tests用于在真实设备或模拟器上测试完整应用或大型模块。
When to Use Integration Tests
适用场景
- Testing complete user flows
- Verifying multiple screens/pages
- Testing navigation flows
- Performance profiling
- 测试完整用户流程
- 验证多页面交互
- 测试导航流程
- 性能分析
Integration Test Structure
Integration测试结构
dart
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);
});
});
}dart
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);
});
});
}Performance Testing
性能测试
dart
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));
});dart
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));
});Advanced Integration Testing
进阶Integration测试
For performance profiling, CI integration, and complex scenarios, see Integration Testing Reference.
关于性能分析、CI集成及复杂场景测试,查看Integration测试参考。
Plugins in Tests
测试中的插件处理
When testing code that uses plugins, special handling is required to avoid crashes.
测试使用插件的代码时,需要特殊处理以避免崩溃。
Testing App Code with Plugins
测试使用插件的应用代码
If your Flutter app uses plugins, you need to mock the platform channel calls in unit/widget tests.
dart
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,
);
});
}如果你的Flutter应用使用了插件,在单元/Widget测试中需要模拟平台通道调用。
dart
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,
);
});
}Testing Plugins
插件测试
For comprehensive guidance on testing Flutter plugins (including native code), see Plugin Testing Reference.
关于Flutter插件(包含原生代码)的全面测试指导,查看插件测试参考。
Common Testing Errors
常见测试错误
'A RenderFlex overflowed...'
'A RenderFlex overflowed...'
Yellow and black stripes indicate overflow. Usually caused by unconstrained children in Row/Column.
Solution: Wrap the overflowing widget in or .
ExpandedFlexibledart
// 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...')])),
],
)黄黑条纹表示布局溢出,通常是Row/Column中的子组件未受约束导致。
解决方案: 将溢出的Widget包裹在或中。
ExpandedFlexibledart
// 问题代码
Row(
children: [
Icon(Icons.message),
Column(children: [Text('Very long text...')]), // 溢出!
],
)
// 修复代码
Row(
children: [
Icon(Icons.message),
Expanded(child: Column(children: [Text('Very long text...')])),
],
)'Vertical viewport was given unbounded height'
'Vertical viewport was given unbounded height'
Occurs when ListView (or other scrollable) is inside Column without height constraints.
Solution: Wrap in or use .
ExpandedshrinkWrap: truedart
// Problem
Column(
children: [
Text('Header'),
ListView(children: [...]), // Error!
],
)
// Solution
Column(
children: [
Text('Header'),
Expanded(child: ListView(children: [...])),
],
)当ListView(或其他可滚动组件)被嵌套在无高度约束的Column中时会触发该错误。
解决方案: 包裹在中或设置。
ExpandedshrinkWrap: truedart
// 问题代码
Column(
children: [
Text('Header'),
ListView(children: [...]), // 错误!
],
)
// 修复代码
Column(
children: [
Text('Header'),
Expanded(child: ListView(children: [...])),
],
)'setState called during build'
'setState called during build'
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.
绝不能在build方法中调用setState。
解决方案: 使用Navigator API或延迟到build完成后再执行。
更多错误及解决方案,查看常见错误参考。
Testing Best Practices
测试最佳实践
- Test Pyramid - More unit/widget tests, fewer integration tests
- Descriptive Test Names - Names should clearly describe what and why
- Arrange-Act-Assert - Structure tests with clear sections
- Avoid Test Interdependence - Each test should be independent
- Mock External Dependencies - Keep tests fast and reliable
- Run Tests in CI - Automate testing on every push
- 测试金字塔 - 多编写单元/Widget测试,减少集成测试数量
- 清晰的测试命名 - 测试名称应明确描述测试内容与目的
- Arrange-Act-Assert结构 - 按准备-执行-断言的结构组织测试
- 测试独立性 - 每个测试应独立运行,不依赖其他测试结果
- 模拟外部依赖 - 保证测试快速且可靠
- CI中自动运行测试 - 每次代码推送时自动执行测试
Running Tests
运行测试
Run All Tests
运行所有测试
bash
flutter testbash
flutter testRun Specific Test File
运行指定测试文件
bash
flutter test test/widget_test.dartbash
flutter test test/widget_test.dartRun Integration Tests
运行集成测试
bash
flutter test integration_test/bash
flutter test integration_test/Run with Coverage
生成测试覆盖率报告
bash
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.htmlbash
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.htmlRun Tests on Different Platforms
在不同平台运行测试
bash
undefinedbash
undefinedAndroid
Android平台
flutter test --platform android
flutter test --platform android
iOS
iOS平台
flutter test --platform ios
flutter test --platform ios
Web
Web平台
flutter test --platform chrome
undefinedflutter test --platform chrome
undefinedDebugging Tests
调试测试
Debug a Test
调试单个测试
bash
flutter test --no-sound-null-safety test/my_test.dartbash
flutter test --no-sound-null-safety test/my_test.dartVerbose Output
详细输出模式
bash
flutter test --verbosebash
flutter test --verboseRun Specific Test
运行指定名称的测试
bash
flutter test --name "Counter value should be incremented"bash
flutter test --name "Counter value should be incremented"Resources
资源
Reference Files
参考文档
- Unit Testing Guide - In-depth unit testing patterns and mocking strategies
- Widget Testing Guide - Widget finding, interactions, and advanced scenarios
- Integration Testing Guide - End-to-end testing and performance profiling
- Mocking Guide - Mocking dependencies and plugin interactions
- Common Errors - Solutions for frequent testing errors
- Plugin Testing - Testing Flutter plugins with native code
- Unit测试指南 - 深入的单元测试模式与模拟策略
- Widget测试指南 - Widget查找、交互及进阶场景
- Integration测试指南 - 端到端测试与性能分析
- 模拟指南 - 依赖项与插件交互模拟
- 常见错误 - 常见测试错误解决方案
- 插件测试 - 包含原生代码的Flutter插件测试