상향식 통합 테스트를 위한 테스트 드라이버 설계
테스트 드라이버의 역할과 구성, 상향식 통합 테스트에서의 활용 방식, 스텁과의 차이를 정리한다.
2026-08-14 · 최초 발행 2025-05-23
상위 모듈이 없을 때 하위 모듈을 검증하는 방법
테스트 드라이버(Test Driver)는 상향식(Bottom-Up) 테스트에서 테스트 대상 하위 모듈을 호출하기 위해 작성하는 프로그램이다. 개발 중이거나 완성된 모듈에 입력을 전달하고, 반환 결과를 수집해 기대값과 비교하는 실행 환경 역할을 맡는다.
통합 과정에서는 개별 기능의 동작뿐 아니라 모듈 인터페이스와 데이터 흐름도 확인해야 한다. 상위 모듈이 아직 구현되지 않았거나 상위 모듈과 결합하기 전에 하위 모듈을 먼저 검증해야 할 때 테스트 드라이버를 사용한다.
드라이버가 관리하는 테스트 환경
드라이버는 단순히 대상 함수를 호출하는 코드에 그치지 않는다. 정상값, 경계값, 예외값을 포함한 입력 데이터를 만들고, 매개변수 전달과 호출 순서를 제어한다. 실행 결과는 예상 결과와 비교하며, 오류가 발생했을 때는 로깅과 보고를 수행할 수 있어야 한다.
테스트 시나리오의 순차 실행, 테스트 환경 초기화, 실행 후 정리도 드라이버가 담당할 수 있다. 따라서 대상 모듈의 API, 매개변수, 반환값 형식을 정확히 이해하고 설계 명세서 및 API 문서를 참조해야 한다.
외부 환경에 의존하지 않도록 설계하고, 필요할 경우 모의 객체(Mock)를 활용한다. 배치 실행과 결과 보고가 가능하도록 구성하면 CI/CD 파이프라인에도 통합할 수 있다.
직접 실행하는 드라이버와 프레임워크 기반 테스트
간단한 검증에는 main 메서드에서 환경을 준비하고 대상 모듈을 호출하는 방식이 사용될 수 있다. 아래 코드는 정상 입력과 경계값을 실행한 뒤 테스트 환경을 정리한다.
public class TestDriver {
public static void main(String[] args) {
// 테스트 환경 설정
setupTestEnvironment();
// 테스트 대상 모듈 인스턴스 생성
TargetModule module = new TargetModule();
// 테스트 케이스 1: 정상 입력값
int result1 = module.calculate(10, 20);
System.out.println("테스트 케이스 1 결과: " + (result1 == 30 ? "성공" : "실패"));
// 테스트 케이스 2: 경계값
int result2 = module.calculate(Integer.MAX_VALUE, 1);
System.out.println("테스트 케이스 2 결과: " + (result2 == Integer.MIN_VALUE ? "성공" : "실패"));
// 테스트 환경 정리
cleanupTestEnvironment();
}
private static void setupTestEnvironment() {
// 테스트 환경 초기화 로직
}
private static void cleanupTestEnvironment() {
// 테스트 후 정리 작업
}
}
JUnit 같은 프레임워크를 사용하면 초기화와 정리, 정상·경계·예외 상황 검증을 테스트 코드 구조 안에 배치할 수 있다.
import org.junit.Before;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
public class TargetModuleTest {
private TargetModule module;
@Before
public void setup() {
// 테스트 전 초기화
module = new TargetModule();
}
@Test
public void testNormalInput() {
// 정상 입력값 테스트
assertEquals(30, module.calculate(10, 20));
}
@Test
public void testBoundaryValue() {
// 경계값 테스트
assertEquals(Integer.MIN_VALUE, module.calculate(Integer.MAX_VALUE, 1));
}
@Test(expected = IllegalArgumentException.class)
public void testInvalidInput() {
// 예외 발생 테스트
module.calculate(-1, -1);
}
@After
public void cleanup() {
// 테스트 후 정리
module = null;
}
}
결제 처리 모듈에 적용한 흐름
카드결제 시스템의 결제 처리 모듈을 검증할 때 드라이버는 다양한 카드 정보와 결제 금액을 시뮬레이션하고, 결제 처리 모듈을 호출한 뒤 결과를 검증한다. 한도 초과나 카드 만료 같은 예외 상황도 이 환경에서 확인할 수 있다.
아래 드라이버는 정상 결제와 만료된 카드를 각각 실행하고, 전체 테스트 결과를 출력한다.
public class PaymentProcessorTestDriver {
public static void main(String[] args) {
PaymentProcessor processor = new PaymentProcessor();
// 테스트 케이스: 정상 결제
CreditCard validCard = new CreditCard("1234-5678-9012-3456", "John Doe", "09/25", "123");
PaymentResult result1 = processor.processPayment(validCard, 1000.0);
assertResult(result1.isSuccessful(), true, "정상 결제 테스트");
// 테스트 케이스: 만료된 카드
CreditCard expiredCard = new CreditCard("1234-5678-9012-3456", "John Doe", "09/20", "123");
PaymentResult result2 = processor.processPayment(expiredCard, 1000.0);
assertResult(result2.isSuccessful(), false, "만료 카드 테스트");
// 테스트 결과 요약 출력
System.out.println("총 테스트: " + totalTests + ", 성공: " + passedTests + ", 실패: " + (totalTests - passedTests));
}
private static int totalTests = 0;
private static int passedTests = 0;
private static void assertResult(boolean actual, boolean expected, String testName) {
totalTests++;
if (actual == expected) {
passedTests++;
System.out.println("[성공] " + testName);
} else {
System.out.println("[실패] " + testName + " - 예상: " + expected + ", 실제: " + actual);
}
}
}
스텁과 드라이버가 맡는 위치
테스트 드라이버와 테스트 스텁(Test Stub)은 모두 통합 테스트의 빈자리를 메우지만 방향이 다르다. 드라이버는 상향식 테스트에서 하위 모듈을 호출하는 상위 컴포넌트 역할을 한다. 입력을 제공하고 출력 결과를 검증하는 쪽이다.
반면 스텁은 하향식 테스트에서 아직 구현되지 않은 하위 모듈을 대신하는 임시 코드다. 상위 모듈을 테스트할 수 있도록 하위 모듈의 동작을 시뮬레이션한다.
자동화 환경으로 확장되는 테스트 드라이버
테스트 드라이버는 JUnit, TestNG, Pytest 같은 자동화 테스트 프레임워크와 통합될 수 있으며, CI/CD 파이프라인 안에서 자동 실행하도록 구성할 수 있다.
머신러닝을 활용한 테스트 케이스 자동 생성, 코드 커버리지 최적화와 예외 케이스 발견도 발전 방향에 포함된다. GUI 기반 드라이버는 테스트 구성과 실행, 결과 시각화 및 분석을 지원하며, 클라우드 기반 분산 테스트는 다양한 환경에서 병렬 실행하고 확장 가능한 테스트 인프라를 구축하는 데 사용된다.
테스트 목적과 범위를 명확히 정하고, 정상·경계·예외 케이스를 충분히 설계해야 한다. 자동화와 재사용성을 고려하면서 결과 검증과 보고 메커니즘을 강화하고, 테스트 코드 자체도 품질 관리 대상에 포함한다. 개발 초기부터 드라이버를 설계하면 개별 모듈의 품질을 통합 이전에 확인하고 지속적인 통합 및 테스트 문화를 뒷받침할 수 있다.