Flutter TDD 마스터 클래스: 결함 없는 앱을 만드는 테스트 주도 개발 전략

지속적인 업데이트와 기능 추가 속에서 여러분의 앱은 안전한가요? ‘하나를 고치면 두 개가 고장 나는’ 공포는 모든 개발자의 숙명과도 같습니다. 하지만 이 공포를 자신감으로 바꿀 수 있는 유일한 해결책이 있습니다. 바로 **TDD(Test-Driven Development)**입니다. 오늘은 Flutter 프로젝트에 TDD를 어떻게 녹여내어 ‘견고한 성벽’ 같은 코드를 구축할 수 있는지 깊이 있게 다뤄보겠습니다.

TDD Red Green Refactor

[그림 1] TDD의 핵심 리듬: Red-Green-Refactor 사이클

1. TDD의 리듬: 실패하고, 성공하고, 개선하라

TDD는 코드를 짜기 전 ‘실패하는 테스트’를 먼저 작성하는 것부터 시작합니다. 이 과정이 처음에는 답답하게 느껴질 수 있습니다. 하지만 이 방식은 두 가지 큰 이점을 줍니다.
1. **요구사항의 명확화**: 테스트를 먼저 짜려면 기능을 완벽히 이해해야 합니다.
2. **과잉 설계 방지**: 테스트를 통과할 만큼의 ‘최소한의 코드’만 작성하게 됩니다.

먼저 실패하는 테스트(**Red**)를 작성하고, 이를 빠르게 통과시키는 코드(**Green**)를 만든 뒤, 중복을 제거하고 구조를 개선하는 **Refactor** 과정을 거칩니다. 이 사이클이 반복될수록 시스템의 신뢰도는 기하수급적으로 올라갑니다.

2. Flutter 테스트 피라미드: 가성비 좋은 테스트 설계

모든 것을 통합 테스트로 짤 수는 없습니다. 효율적인 테스트를 위해 **테스트 피라미드(Testing Pyramid)** 전략이 필요합니다.

Testing Pyramid

[그림 2] Flutter 테스트의 세 계층: Unit, Widget, Integration

– **Unit Tests (기반)**: 비즈니스 로직, 함수 등 가장 작은 단위를 테스트합니다. 실행 속도가 가장 빠르며 피라미드의 가장 넓은 면적을 차지해야 합니다.
– **Widget Tests (중간)**: 위젯이 화면에 의도대로 그려지는지, 버튼 클릭 시 상호작용이 일어나는지 확인합니다.
– **Integration Tests (정상)**: 앱 전체가 실제 디바이스 환경에서 서버와 통신하며 잘 작동하는지 확인합니다. 가장 가치가 높지만 속도가 느리고 비용이 많이 듭니다.

3. 실전 TDD 예시: API 결과 처리 로직

가장 흔한 시나리오인 ‘데이터 로딩 성공/실패’를 TDD로 구현해 봅시다.

// 1. [Red] 실패하는 테스트 먼저 작성
test('API 호출 성공 시 사용자 목록을 반환해야 함', () async {
  final mockRepo = MockUserRepository();
  when(mockRepo.getUsers()).thenAnswer((_) async => [User(id: '1')]);
  
  final useCase = GetUsersUseCase(mockRepo);
  final result = await useCase.execute();
  
  expect(result.length, 1);
});

// 2. [Green] 테스트를 통과시킬 최소한의 코드 작성
class GetUsersUseCase {
  final UserRepository repository;
  GetUsersUseCase(this.repository);
  Future<List<User>> execute() => repository.getUsers();
}

이런 식으로 `Mockito` 라이브러리를 활용해 외부 의존성을 격리(Mocking)하면, 실제 서버 없이도 로직의 완결성을 100% 검증할 수 있습니다.

4. 100% 코드 커버리지가 정답인가?

Code Coverage Report

[그림 3] 완벽한 테스트 통과 결과와 커버리지 리포트의 예시

높은 코드 커버리지는 심리적 안정감을 줍니다. 하지만 **’숫자’에만 집착한 커버리지는 위험**합니다. 단순한 Getter/Setter를 테스트하느라 정작 중요한 엣지 케이스(Edge Case)를 놓치고 있지는 않은지 늘 경계해야 합니다. 의미 있는 핵심 로직에 집중하는 것이 엘리트 개발자의 테스트 전략입니다.

마치며: 테스트는 비용이 아니라 투자입니다

“바빠서 테스트 짤 시간이 없어요”라고 말하는 개발자가 많습니다. 하지만 테스트가 없는 코드는 나중에 더 큰 ‘이자’청구서(버그 수정을 위한 야근)로 돌아옵니다. 오늘부터 아주 작은 함수 하나라도 TDD로 시작해 보세요. 그 작은 변화가 여러분의 개발 인생을 ‘불안’에서 ‘자신감’으로 바꿔놓을 것입니다.

댓글 남기기