name: flutter-testing description: Flutter の flutter_test + mocktail によるユニット/Widget テスト、integration_test による統合テスト、TDD ワークフローの汎用ベストプラクティス。プロジェクト固有のレイヤー別テスト戦略は PROJECT.md を参照。
Flutter テスト(汎用)
Flutter + Dart でのテスト駆動開発と汎用テストパターン。
プロジェクト固有のレイヤー別テスト戦略(テスト対象のレイヤー構成、各層のテスト方針)は PROJECT.md を参照してください。
いつ有効化するか
- テストを書く/修正するとき
- テストカバレッジの向上時
テスト環境
dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^1.0.0
integration_test:
sdk: flutter
ユニットテスト (mocktail)
class MockUserRepository extends Mock implements UserRepository {}
void main() {
late MockUserRepository mockRepo;
setUp(() {
mockRepo = MockUserRepository();
});
test('正常系: データを返す', () async {
when(() => mockRepo.getById('1'))
.thenAnswer((_) async => User(id: '1', name: 'Test'));
final result = await mockRepo.getById('1');
expect(result.name, equals('Test'));
verify(() => mockRepo.getById('1')).called(1);
});
}
Widget テスト
testWidgets('ユーザー情報を表示する', (tester) async {
await tester.pumpWidget(
MaterialApp(home: Scaffold(body: UserCard(user: testUser))),
);
expect(find.text('Alice'), findsOneWidget);
});
testWidgets('バリデーションエラーを表示する', (tester) async {
await tester.pumpWidget(MaterialApp(home: LoginForm()));
await tester.tap(find.text('送信'));
await tester.pump();
expect(find.text('必須項目です'), findsOneWidget);
});
統合テスト
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('ログインフロー', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(find.byKey(const Key('email')), 'test@example.com');
await tester.enterText(find.byKey(const Key('password')), 'pass');
await tester.tap(find.text('ログイン'));
await tester.pumpAndSettle();
expect(find.text('ホーム'), findsOneWidget);
});
}
テストヘルパー
extension PumpApp on WidgetTester {
Future<void> pumpApp(Widget widget, {List<Override> overrides = const []}) async {
await pumpWidget(
ProviderScope(
overrides: overrides,
child: MaterialApp(home: Scaffold(body: widget)),
),
);
}
}
実行コマンド
テスト実行コマンドは PROJECT.md のビルド・テストコマンドセクションを参照。