출시·고도화 중
Flutter 안내서 · 5/6
Flutter는 테스트 도구를 SDK에 기본으로 포함합니다. 테스트는 범위에 따라 세 가지로 나뉩니다. 단위 테스트는 함수나 클래스 하나를, 위젯 테스트는 위젯 하나 또는 작은 화면을, 통합 테스트는 실제 기기나 에뮬레이터에서 앱 전체를 검사합니다. 아래로 갈수록 실제에 가깝지만 느리므로, 단위·위젯 테스트를 많이 두고 통합 테스트는 핵심 흐름에만 씁니다.
| 종류 | 대상 | 실행 환경 | 속도 |
|---|---|---|---|
| 단위 테스트 | 함수, 클래스, 상태 로직 | Dart VM | 매우 빠름 |
| 위젯 테스트 | 위젯, 화면 일부 | 가상 테스트 환경 | 빠름 |
| 통합 테스트 | 앱 전체 흐름 | 실제 기기·에뮬레이터·데스크톱 | 느림 |
테스트 파일은 test 폴더에 두고 이름을 _test.dart로 끝냅니다. Flutter 프로젝트에서는 flutter_test 패키지가 test, group, expect 같은 함수를 함께 제공합니다.
// lib/cart.dart
class Cart {
final Map<String, int> _prices = {};
void add(String name, int price) => _prices[name] = price;
int get total => _prices.values.fold(0, (sum, price) => sum + price);
}// test/cart_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hello_app/cart.dart';
void main() {
group('Cart', () {
test('처음에는 합계가 0이다', () {
expect(Cart().total, 0);
});
test('상품을 추가하면 합계가 늘어난다', () {
final cart = Cart()
..add('사과', 1000)
..add('배', 2500);
expect(cart.total, 3500);
});
});
}테스트는 명령줄이나 편집기에서 실행합니다.
# 모든 테스트 실행
flutter test
# 파일 하나만 실행
flutter test test/cart_test.dart
# 이름으로 골라 실행
flutter test --name "합계"
# 커버리지 수집(coverage/lcov.info 생성)
flutter test --coverage위젯 테스트는 testWidgets로 작성합니다. WidgetTester로 위젯을 화면에 올리고(pumpWidget), 찾고(find), 누른 뒤(tap), 다시 그리게 하고(pump), 결과를 확인합니다. 새 프로젝트의 test/widget_test.dart가 바로 이런 테스트입니다.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hello_app/main.dart';
void main() {
testWidgets('더하기 버튼을 누르면 숫자가 올라간다', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}자주 쓰는 도구는 다음과 같습니다.
find.text, find.byType, find.byIcon, find.byKey: 위젯 찾기. 테스트에서 찾기 쉽도록 위젯에 Key('submit') 같은 키를 붙여 둘 수 있습니다.findsOneWidget, findsNothing, findsNWidgets(n): 찾은 개수 확인tester.enterText(finder, '글자'): 입력란에 글자 넣기tester.pumpAndSettle(): 애니메이션이 모두 끝날 때까지 다시 그리기네트워크를 쓰는 코드를 테스트할 때는 실제 서버 대신 가짜 응답을 씁니다. http 패키지는 테스트용 MockClient를 제공하므로, 함수가 http.Client를 인자로 받도록 만들면 쉽게 바꿔 끼울 수 있습니다. 더 복잡한 경우에는 mocktail이나 mockito 패키지로 가짜 객체를 만듭니다.
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:flutter_test/flutter_test.dart';
Future<String> fetchTitle(http.Client client) async {
final response = await client.get(Uri.parse('https://example.com/title'));
return response.body;
}
void main() {
test('서버 응답의 본문을 돌려준다', () async {
final client = MockClient((request) async => http.Response('Flutter', 200));
expect(await fetchTitle(client), 'Flutter');
});
}통합 테스트는 SDK에 포함된 integration_test 패키지로 작성하며, 실제 기기·에뮬레이터·데스크톱에서 앱을 실행해 위젯 테스트와 같은 API로 조작합니다. 먼저 개발용 의존성을 추가합니다.
flutter pub add 'dev:integration_test:{"sdk":"flutter"}'테스트 파일은 프로젝트 루트의 integration_test 폴더에 둡니다.
// integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:hello_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('앱 전체 흐름', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});
}# 연결된 기기에서 실행
flutter test integration_test/app_test.dart
# 기기를 지정
flutter test integration_test -d windowsflutter test로 실행하며 --coverage로 커버리지를 모을 수 있습니다.pumpWidget, find, tap, pump, expect의 흐름으로 작성합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.