Test Code Review

박진·2026년 2월 3일

2026.02.03 (화)
리펙토링하면서 테스트 코드 다시 손보기
테스트 코드 리뷰


Flutter test 종류 3

1. 단위 테스트 (Unit tests)

단위 테스트는 메서드나 클래스의 동작을 확인, 특정 코드 단위를 분리하고 테스트 하기 때문에 다른 부분의 영향을 받지 않고 테스트할 수 있고 잘 작동하는지 확인 가능
목적: 특정 코드 단위의 동작을 확인해서 코드의 품질을 개선하고 버그를 방지

2. 위젯 테스트 (Widget tests)

위젯 테스트는 UI 요소의 개별 동작을 테스트, 버튼이 제대로 클릭되는지 텍스트 필드에 입력이 제대로 반영되는지 등을 확인함. 주로 위젯의 렌더링, 상태변화, 사용자 입력 등을 테스트

3. 통합 테스트 (Integration tests)

통합 테스트는 애플리케이션의 여러 부분을 통합하여 전체 애플리케이션의 동작을 테스트함. 여러 위젯이나 화면 간의 상호 작용 및 전환, 데이트 흐름 등을 테스트함. 실제 디바이스나 시뮬레이터를 실행해서 앱의 실제 환경에서 테스트를 진행함.


리펙토링한 테스트 코드

1. ui / home / home_screen_test.dart

-> HomeScreen 위젯이 날씨 데이터 로딩 상태에 따라 사용자가 보는 화면 (ui)가 잘 그려지는지 확인하기 위한 테스트 코드

  • MockWeatherRepository 클래스는 WeatherRepository를 흉내 내는 가짜 클래스

  • 테스트에서 사용할 가짜 저장소를 선언합니다. late는 나중에(setUp에서) 초기화하겠다는 의미

  • setUp -> 각 테스트 케이스가 실행되기 전 항상 실행되는 블록!
    매 테스트마다 새로운 객체를 생성해 테스트 간 간섭을 방지

  • 테스트 할 위젯을 감싸서 반환하는 헬퍼 함수

  • 여기서 Rivderpod의 핵심 기능인데요, 실제 Provider가 mockRepositoryfmf 바라보도록 덮어씌움

테스트 전용 주요 메서드

when(... ~ .thenAnswer)

만약 이 함수가 ~ 실행되면, ~이렇게 대답해. 행동 정의 메서드
사용 방법은 when(~~~.thenAnwser)

any()

어떤 인자 값이 들어오든 상관없이 매칭하겠다 라는 의미

tester.pubWidget()

주어진 위젯을 테스트 환경의 가상 화면에 렌더링

tester.pumpAndSettle()

애니메이션이나 연속적인 프레임 변화가 모두 멈출 때까지 반복해서 새로고침


2. ui / home / home_view_model_test.dart

-> 위에 살펴본 코드가 화면이 어떻게 보이는가를 확인하는 위젯 테스트였다면 이번 코드는 화면 없이 데이터 로직이 정확히 계산되고 상태를 잘 바꾸는가를 확인하는 단위 테스트

테스트 전용 주요 메서드

ProviderContainer

UI 없이 Provider의 상태를 관리하는 엔진. 단위 테스트의 핵심 도구임

addTearDown

테스트가 성공하든 실패하든 마지막에 꼭 실행해야 할 정리 작업을 등록

container.listen

상태가 변할 때마다 특정 동작을 수행하게 합니다. UI의 ref.listen과 비슷

verify(...).called(n)

"이 함수가 정말 n번 실행됐어?"라고 확인하는 감사(Audit) 기능


Mocking (가짜 객체 사용)의 이점

  • 격리성: 실제 API 서버가 점검 중잉거나 인터넷이 안 되어도 테스트를 수행 할 수 있다.
  • 속도 : 실제 네트워크 통신을 기다리지 않으므로 테스트 속도가 빠르다
  • 통제 가능: 에러 상황이나 로딩 중인 상황 등 현실에서 재현하기 어려운 시나리오를 강제로 만들어서 테스트 할 수 있다.

Riverpod overrides의 이점

  • 코드 수정 최소화: UI 코드 (HomeScreen)을 전혀 수정하지 않고, 테스트 코드에서만 데이터를 바꿔치기 할 수 있어 깔끔한 아키텍처를 유지해줍니다.

상태별 분리 테스트 이점

로딩 -> 성공 -> 에러 이어지는 사용자 경험(UX)의 모든 흐름을 코드로 확인할 수 있다.
나중에 UI 를 수정하더라도 테스트만 통과하면 기능은 여전히 잘 작동한다 라는 홗신을 가질 수 있다.


ProviderContainer 사용의 이점

  • 순수 로직 집중: 화면 렌더링(버튼, 텍스트 등)에 신경 쓰지 않고 오직 데이터가 Loading -> Data 또는 Loading -> Error로 잘 변하는지만 확인할 수 있습니다.
    리소스 절약: 위젯 테스트보다 훨씬 빠르고 가볍게 실행됩니다.

addTearDown의 이점

테스트마다 깨끗한 환경을 보장합니다. 이전 테스트에서 썼던 데이터가 다음 테스트에 영향을 주지 않도록 막아줍니다.


Flutter 테스트 작성 5단계 프로세스

① 테스트 목적 정의 (test 함수)

무엇을 테스트할지 명확한 문장으로 적어보기

② 환경 설정 및 가짜 객체 주입 (Arrange)

Mock(가짜) 객체를 만들고, 필요한 데이터를 미리 심어두기

사용 도구: Mocktail, Mockito, ProviderContainer

③ 동작 수행 (Act)

테스트 대상이 되는 기능을 실행하기

Unit Test: 함수 실행 (calculator.add(1, 2))

Widget Test: 위젯 렌더링 및 클릭 (tester.pumpWidget(), tester.tap())

④ 결과 확인 (Assert)

expect 함수를 사용해 실제 값(Actual)과 기대 값(Matcher)을 비교
expect(결과값, 3);
expect(find.text('성공'), findsOneWidget);

⑤ 정리 (TearDown)

테스트가 끝난 후 사용한 리소스를 해제해주기
addTearDown(() => container.dispose());

반드시 해야 하는 경우: ProviderContainer, StreamController, TextEditingController, AnimationController 등을 생성했을 때.

안 해도 되는 경우: 단순히 숫자나 문자열을 계산하는 순수 함수(Pure Function) 테스트일 때.


초보자를 위한 작성 팁 💡

한 번에 하나만 테스트
하나의 test 블록 안에서 너무 많은 것을 검증하려고 하면, 실패했을 때 원인을 찾기 힘들다

실패하는 케이스도 테스트해보기
"정상 작동"뿐만 아니라 "에러가 났을 때 화면에 에러 메시지가 잘 뜨는지" 확인하는 것이 더 중요한 경우가 많다

테스트 가능한 코드(Testable Code)를 만들기
코드를 짤 때부터 "이걸 나중에 어떻게 테스트하지?"라고 고민하면 자연스럽게 의존성이 분리된 깨끗한 코드(Clean Code)가 나오게 되어있따~

0개의 댓글