Flutter TDD (Test-Driven Development) 심화 가이드
TDD(Test-Driven Development)는 테스트 코드를 먼저 작성한 후, 테스트를 통과하도록 실제 코드를 작성하는 개발 방식입니다. Flutter에서 TDD를 활용하면 안정적이고 유지보수가 용이한 코드를 작성할 수 있습니다. 이번 심화 가이드에서는 TDD의 원칙, Flutter에서의 적용 방법, 그리고 실전 예제를 중심으로 설명합니다.
1. TDD의 3단계
TDD는 다음 세 가지 단계를 반복하는 개발 방식입니다:
- Red: 실패하는 테스트를 작성합니다.
- Green: 테스트를 통과하도록 최소한의 코드를 작성합니다.
- Refactor: 중복 제거 및 성능 개선을 위한 리팩토링을 수행합니다.
2. Flutter에서 TDD 적용 준비
필요한 패키지 설치
Flutter에서 TDD를 진행하려면 아래와 같은 패키지가 필요합니다:
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.17
build_runner: ^2.3.3
- flutter_test: 기본적인 테스트 작성.
- mockito: 의존성 주입 및 목(mock) 객체 생성.
- build_runner: Mock 클래스를 자동 생성.
파일 구조
TDD를 효율적으로 진행하기 위해 파일을 명확히 분리하세요:
lib/
- models/
- services/
- repositories/
- widgets/
test/
- unit/
- widget/
- integration/
3. 실전 예제: 뉴스 앱 개발
요구사항
사용자가 뉴스를 검색하면 관련된 뉴스 기사가 표시됩니다. 이 기능을 구현하기 위해 TDD를 적용합니다.
3.1. Red 단계: 실패하는 테스트 작성
먼저, 뉴스 검색 기능의 리포지토리를 테스트합니다:
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:my_app/repositories/news_repository.dart';
import 'package:my_app/models/news.dart';
@GenerateMocks([NewsRepository])
void main() {
late MockNewsRepository mockNewsRepository;
setUp(() {
mockNewsRepository = MockNewsRepository();
});
test('should return a list of news articles when search is successful', () async {
// Arrange
final newsList = [
News(title: 'Test News 1', content: 'Content 1'),
News(title: 'Test News 2', content: 'Content 2'),
];
when(mockNewsRepository.searchNews('flutter')).thenAnswer((_) async => newsList);
// Act
final result = await mockNewsRepository.searchNews('flutter');
// Assert
expect(result, newsList);
});
}
3.2. Green 단계: 최소한의 코드 작성
NewsRepository에 대해 테스트를 통과하도록 구현합니다:
class NewsRepository {
Future<List<News>> searchNews(String query) async {
// 예제: 실제 API 호출 로직 대체
return [
News(title: 'Test News 1', content: 'Content 1'),
News(title: 'Test News 2', content: 'Content 2'),
];
}
}
테스트를 실행하여 성공 여부를 확인합니다:
flutter test
3.3. Refactor 단계: 리팩토링
- 중복 코드 제거.
- 에러 처리 및 로깅 추가.
4. Widget 테스트에서의 TDD
시나리오
뉴스 검색 결과를 보여주는 NewsListWidget에 대해 TDD를 적용합니다.
- Red: 테스트 작성
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:my_app/widgets/news_list_widget.dart';
import 'package:my_app/models/news.dart';
void main() {
testWidgets('should display news articles', (WidgetTester tester) async {
// Arrange
final newsList = [
News(title: 'Test News 1', content: 'Content 1'),
News(title: 'Test News 2', content: 'Content 2'),
];
// Act
await tester.pumpWidget(MaterialApp(
home: NewsListWidget(news: newsList),
));
// Assert
expect(find.text('Test News 1'), findsOneWidget);
expect(find.text('Test News 2'), findsOneWidget);
});
}
- Green: 위젯 구현
class NewsListWidget extends StatelessWidget {
final List<News> news;
NewsListWidget({required this.news});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: news.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(news[index].title),
subtitle: Text(news[index].content),
);
},
);
}
}
- Refactor: 위젯 스타일 개선 및 테스트 코드 정리.
5. 통합 테스트에서의 TDD
시나리오
전체 뉴스 검색 플로우를 통합 테스트로 작성합니다.
- Red: 통합 테스트 작성
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {
testWidgets('should complete search flow', (WidgetTester tester) async {
// Act
await tester.pumpWidget(MyApp());
await tester.enterText(find.byType(TextField), 'flutter');
await tester.tap(find.byType(IconButton));
await tester.pumpAndSettle();
// Assert
expect(find.text('Test News 1'), findsOneWidget);
expect(find.text('Test News 2'), findsOneWidget);
});
}
- Green: 앱 전체 플로우 구현.
- Refactor: 코드와 테스트 개선.
결론
Flutter에서 TDD를 적용하면 더 신뢰성 있고 유지보수 가능한 코드를 작성할 수 있습니다.
- Red-Green-Refactor의 사이클을 철저히 지키세요.
- 유닛, 위젯, 통합 테스트의 적절한 조합을 활용하세요.
- 지속적인 연습을 통해 더 효율적으로 TDD를 적용할 수 있습니다.
TDD를 통해 Flutter 프로젝트의 품질을 한 단계 끌어올려 보세요!
'Flutter' 카테고리의 다른 글
Flutter 프로젝트에서 클린 코드 작성하기 (0) | 2025.01.24 |
---|---|
Flutter에서 Push Notification 심화 가이드 (0) | 2025.01.23 |
Flutter에서 Dio를 사용한 네트워크 통신 심화 가이드 (0) | 2025.01.21 |
Flutter에서 SQLite 사용하기 (0) | 2025.01.21 |
Flutter 프로젝트에서 Fastlane 설정하기 (1) | 2025.01.20 |