
안녕하세요, 성조입니다.
이번 포스팅에서는 플러터에서 Unit Test를 다루는 시간을 가져보려 해요.
Unit Test란?
함수, 메서드, 클래스처럼 가장 작은 단위의 코드가 의도한 대로 동작하는지 검증하는 테스트이다.
한 번 정리하면 앱 전체를 실행해서 확인하기 전에 함수, 클래스, 메서드 등이 정말 의도한 값처럼 동작하는지 반환, 처리, 변경 등을 자동으로 확인하는 테스트 과정이다.
유닛 테스트는 앱 화면을 직접 띄우거나, 사용자 인터페이스를 눌러보는 테스트와는 다르게 로직 자체를 검증하는 것에 초점이 잡히는 테스트이고, 유닛 테스트는 대상들이 다양하게 있는데 덧셈, 할인, 문자열을 가공하는 함수, 로그인 상태 검증, 예외 처리 로직, 컨트롤러 등의 작고 독립적인 로직 대상들을 빠르게 검증하는 것이 유닛 테스트이다.
Unit Test가 잘 구성되어 있으면 어떤 부분이 좋아지는가?
1) 버그를 빨리 찾을 수 있다.
2) 리팩토링이 쉬워진다.
3) 유지보수가 쉬워진다.
4) 커버리지가 높아지면 의존성이 적어지도록 구성하여 로직 설계가 좋아진다.
개념 이해하기 AAA 패턴
테스트 코드 내부(test body)를 세 가지 단계로 나누어 작성하는 방법이고, 아래 유닛 테스트 기본 구조를 사용하기 범위 정의를 미리 이해해야 된다.
1) Arrane (준비): 테스트를 진행하기 위한 환경, 객체, 초기 데이터 등을 준비한다.
2) Act (실행): 진짜 테스트하고 싶은 기능(함수나 메서드)을 하나만 실행.
3) Assert (검증): 실행된 결과를 가져와서, 초기 준비네 기대했던 정답과 일치하는지 expect()를 사용해 확인한다.
메서드 한 스푼 이해하기
1) test() -> 설명을 작성하는 메서드다
2) expect() -> 결과를 검증하는 메서드다.
3) group() -> 관련 테스트들을 하나의 그룹으로 묶는 메서드다.
4) setUp() -> 반복적으로 필요한 준비 작업을 처리하는 메서드다.
5) tearDown() -> 각 테스트가 끝난 뒤 정리 작업할 때 사용하는 메서드다.
6) 검증 모음 -> isTrue, isFalse, isNull, isNotNull, isA<String>, contains('포함된 게 맞는지 검증') 검증을 위한 값들이다.
유닛 테스트 기본 구조
import 'package:flutter_test/flutter_test.dart';
// --------------------------------------------------
// [우리가 만든 실제 코드 - 테스트 대상] 예시는 증감 코드
// --------------------------------------------------
class Counter {
int value = 0; // 초기값은 0
void increment() {
value++; // 값을 1 증가시키는 함수
}
}
// --------------------------------------------------
// [테스트 코드]
// --------------------------------------------------
void main() {
test('Counter의 increment를 호출하면 값이 1 증가해야 한다', () {
// 1. Arrange (준비)
// 테스트할 Counter 객체를 새로 만든다. (초기값 0인 상태)
final counter = Counter();
// 2. Act (실행)
// 테스트하고 싶은 동작을 실행.
counter.increment();
// 3. Assert (검증)
// 기대하는 결과(1)와 실제 값(counter.value)을 비교한다.
expect(counter.value, 1);
});
}
- 코드 이해하기
1) test('설명'): '이 기능은 이렇게 동작되어야 한다.'의 시나리오 정의
2) Arrange: 깨끗한 상태에서 테스트를 시작하기 위해 Counter 객체를 새로 생성한다.
3) Act: 유저가 버튼을 눌렀다고 가정하고 increment() 함수를 호출했다.
4) Assert: expect() 실제 1값이 증가하게 되어서 테스트 값이 맞는지를 판별하는 메서드 확인하기. 이런 형식으로 확인되는 코드이다.
유닛 테스트 사용 팁
1) 테스트 이름은 단순한 제목이 아니라 성공한 케이스와 실패했을 때 원인을 명확하게 보여주는 메시지 역할을 해줘야 한다. -> 이해하기 어려운 테스트 케이스는 협업에 어려움을 발생시키기 때문에 어떤 함수/메서드인지, 어떤 조건으로(input) 어떤 결과가 나와야 하는지(output) 확인되어야 한다.
2) 실무 기준으로는 '하나의 테스트는 하나의 책임만 검증', '테스트는 서로 독립적으로 작동', '성공 케이스와 실패 케이스를 함께 작성', '외부 의존성을 최소화하는 것(네트워크, DB, 파일 시스템 등)', '순수 로직을 분리하기 (계산, 판단, 검증 로직은 별도 클래스나 함수로 분리해서 가져와 사용' 이러한 조건들을 잡는 것이 권장된다.
3) 예외 테스트에서 함수/메서드를 직접 실행시키면 검사전에 테스트가 터져버리는 문제가 있어서 정리되어야 한다.
4) 테스트끼리 상태를 공유하거나 너무 큰 단위를 사용하는 일은 발생되지 않도록 지양하는 것이 좋다.
- 참조 링크 -
영문에 더 익숙하신 분들은 이 링크에 예제가 있으니 더 자세히 확인하는 것을 추천드려요
https://docs.flutter.dev/cookbook/testing/unit/introduction
An introduction to unit testing
How to write unit tests.
docs.flutter.dev
펍데브에서 다양한 테스트 패키지들이 지원됩니다.
test | Dart package
A full featured library for writing and running Dart tests across platforms.
pub.dev
감사합니다.
다음 포스팅에서 뵙겠습니다.
'Flutter' 카테고리의 다른 글
| [Flutter] Color Class 가벼운 사용법 정리 (1) | 2026.03.13 |
|---|---|
| [Flutter] ListView 기초 정리 (0) | 2026.01.03 |
| [Flutter] Flutter Padding class 이해하기 (0) | 2026.01.02 |
| [Flutter] SingleChildScrollView 정리하기 (0) | 2026.01.01 |
| [Flutter] AppLifecycleState 이론 한 스푼 (7) | 2025.08.04 |