본문 바로가기
Flutter

Flutter TDD (Test-Driven Development) 심화 가이드

by 안될개발 2025. 1. 23.

Flutter TDD (Test-Driven Development) 심화 가이드

Flutter TDD (Test-Driven Development) 심화 가이드

TDD(Test-Driven Development)는 테스트 코드를 먼저 작성한 후, 테스트를 통과하도록 실제 코드를 작성하는 개발 방식입니다. Flutter에서 TDD를 활용하면 안정적이고 유지보수가 용이한 코드를 작성할 수 있습니다. 이번 심화 가이드에서는 TDD의 원칙, Flutter에서의 적용 방법, 그리고 실전 예제를 중심으로 설명합니다.


1. TDD의 3단계

TDD는 다음 세 가지 단계를 반복하는 개발 방식입니다:

  1. Red: 실패하는 테스트를 작성합니다.
  2. Green: 테스트를 통과하도록 최소한의 코드를 작성합니다.
  3. 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를 적용합니다.

  1. 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);
  });
}
  1. 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),
        );
      },
    );
  }
}
  1. Refactor: 위젯 스타일 개선 및 테스트 코드 정리.

5. 통합 테스트에서의 TDD

시나리오

전체 뉴스 검색 플로우를 통합 테스트로 작성합니다.

  1. 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);
  });
}
  1. Green: 앱 전체 플로우 구현.
  2. Refactor: 코드와 테스트 개선.

결론

Flutter에서 TDD를 적용하면 더 신뢰성 있고 유지보수 가능한 코드를 작성할 수 있습니다.

  • Red-Green-Refactor의 사이클을 철저히 지키세요.
  • 유닛, 위젯, 통합 테스트의 적절한 조합을 활용하세요.
  • 지속적인 연습을 통해 더 효율적으로 TDD를 적용할 수 있습니다.

TDD를 통해 Flutter 프로젝트의 품질을 한 단계 끌어올려 보세요!