IT기술/플러터 (flutter)

Flutter 테스트 완벽 가이드: 단위/위젯/통합 테스트로 앱 품질 극대화하기

후스파 2025. 7. 12. 06:34
반응형

플러터(Flutter) 앱의 품질과 신뢰성을 높이기 위해서는 단위 테스트(Unit Test), 위젯 테스트(Widget Test), 통합 테스트(Integration Test)를 체계적으로 수행하는 것이 필수입니다.
각 테스트의 목적, 작성 방법, 실전 팁을 한눈에 정리합니다.


단위 테스트 (Unit Test)

개념

  • 코드의 가장 작은 단위(함수, 클래스 등)를 격리하여 동작을 검증하는 테스트
  • 외부 UI, DB, 네트워크 등과 무관하게 로직 자체만 검증

장점

  • 코드 신뢰성·유지보수성 향상
  • 리팩토링 시 안정성 보장
  • 문서화 및 협업 기준 제공

작성 방법

  • test 폴더에 _test.dart 파일 생성
  • flutter_test 패키지(dev_dependencies) 사용
  • test() 함수와 expect()로 결과 검증
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter.dart';

void main() {
  test('Counter의 초기값은 0이어야 한다', () {
    final counter = Counter();
    expect(counter.value, 0);
  });

  test('increment() 호출 시 값이 1 증가해야 한다', () {
    final counter = Counter();
    counter.increment();
    expect(counter.value, 1);
  });
}

터미널에서 flutter test로 실행


위젯 테스트 (Widget Test)

개념

  • 하나의 위젯(컴포넌트) 단위로 UI, 상호작용, 상태 변화를 검증
  • 실제 렌더링 없이 가상 환경에서 동작(빠르고 효율적)

작성 방법

  • flutter_test 패키지의 testWidgets() 사용
  • WidgetTester로 위젯 빌드, 상호작용(pump, tap 등) 시뮬레이션
  • find, expect 등으로 결과 검증
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';

void main() {
  testWidgets('카운터 증가 버튼 테스트', (WidgetTester tester) async {
    await tester.pumpWidget(MyApp());

    // 초기값 0 확인
    expect(find.text('0'), findsOneWidget);

    // + 버튼 클릭
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // 값이 1로 변경됐는지 확인
    expect(find.text('1'), findsOneWidget);
  });
}
  • pumpWidget: 위젯 트리 빌드
  • tap, pump: 상호작용 및 상태 반영

통합 테스트 (Integration Test)

개념

  • 실제 디바이스/에뮬레이터에서 앱의 여러 위젯·기능을 엔드 투 엔드(End-to-End)로 검증
  • 사용자 시나리오(로그인, 화면 전환, 데이터 처리 등) 전체 흐름 테스트

작성 방법

  • integration_test 패키지 사용 (integration_test 폴더 내 _test.dart 파일 작성)
  • 실제 앱 실행, UI 상호작용, 결과 검증
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('로그인 후 메인화면 진입', (WidgetTester tester) async {
    app.main();
    await tester.pumpAndSettle();

    // 로그인 입력 및 버튼 클릭
    await tester.enterText(find.byKey(Key('email')), 'test@example.com');
    await tester.enterText(find.byKey(Key('password')), '1234');
    await tester.tap(find.text('로그인'));
    await tester.pumpAndSettle();

    // 메인화면 진입 확인
    expect(find.text('메인화면'), findsOneWidget);
  });
}
  • 실제 앱 구동, 여러 화면 및 기능 연동 테스트
  • pumpAndSettle: 모든 애니메이션·상태 변화 완료까지 대기

비교 요약 및 실전 팁

단위 테스트함수/클래스매우 빠름로직 검증, 리팩토링
위젯 테스트단일 위젯빠름UI/상호작용, 상태 변화
통합 테스트전체 앱/흐름느림실제 사용자 시나리오

실전 팁

  • 테스트 커버리지: 핵심 로직, UI, 주요 시나리오 모두 포함
  • Mock/Stub 활용: 외부 의존성 격리(특히 단위 테스트)
  • CI/CD 연동: 자동화로 배포 전 품질 확보
  • 테스트는 test, integration_test 폴더에 별도 관리
  • 테스트 함수명, 설명 명확히 작성

고급 테스트 패턴

// Mock 객체 활용 예시
class MockApiService extends Mock implements ApiService {}

void main() {
  test('API 호출 성공 시 데이터 반환', () async {
    final mockApi = MockApiService();
    when(mockApi.fetchData()).thenAnswer((_) async => 'test data');

    final service = DataService(mockApi);
    final result = await service.getData();

    expect(result, 'test data');
  });
}

마무리

플러터의 단위 테스트, 위젯 테스트, 통합 테스트를 적절히 조합하면 앱의 신뢰성과 유지보수성을 크게 높일 수 있습니다. 작은 코드부터 전체 사용자 흐름까지 꼼꼼하게 검증하는 습관이 고품질 앱 개발의 지름길입니다.
테스트 피라미드를 고려하여 단위 테스트를 가장 많이, 통합 테스트를 적절히 배치하고, 지속적인 테스트 자동화를 통해 안정적인 앱 개발 환경을 구축해보세요.

반응형