코딩:개발일지
단위 테스트(Unit Test) vs 통합 테스트(Integration Test) 차이점 정리
ZZJJing
2025. 5. 28. 21:19
반응형
단위 테스트란?
- 가장 작은 단위의 코드를 테스트
- 보통 하나의 메서드나 클래스를 테스트
- 외부 의존성을 Mock으로 대체
- 빠르고 독립적
- TDD의 기본
통합 테스트란?
- 여러 컴포넌트가 함께 동작하는지 테스트
- 실제 DB, API 등 외부 시스템과 연동
- 단위 테스트보다 느림
- 실제 환경과 비슷한 조건에서 테스트
주요 차이점
구분 단위 테스트 통합 테스트
범위 | 개별 메서드/클래스 | 여러 컴포넌트 |
속도 | 빠름 | 느림 |
외부 의존성 | Mock 사용 | 실제 시스템 사용 |
격리성 | 완전 격리 | 부분 격리 |
복잡도 | 단순 | 복잡 |
Java 단위 테스트 예제
테스트 대상 클래스
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("0으로 나눌 수 없습니다");
}
return a / b;
}
}
JUnit5 단위 테스트
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
private Calculator calculator = new Calculator();
@Test
@DisplayName("두 수를 더하면 올바른 결과가 나온다")
void testAdd() {
// Given
int a = 5;
int b = 3;
// When
int result = calculator.add(a, b);
// Then
assertEquals(8, result);
}
@Test
@DisplayName("0으로 나누면 예외가 발생한다")
void testDivideByZero() {
// Given
int a = 10;
int b = 0;
// When & Then
assertThrows(IllegalArgumentException.class,
() -> calculator.divide(a, b));
}
}
Java 통합 테스트 예제
테스트 대상 서비스 클래스
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(String name, String email) {
User user = new User(name, email);
return userRepository.save(user);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
Spring Boot 통합 테스트
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
@Transactional
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
@DisplayName("사용자 생성 후 DB에서 조회 가능하다")
void testCreateAndRetrieveUser() {
// Given
String name = "김개발";
String email = "dev@example.com";
// When
User createdUser = userService.createUser(name, email);
List<User> allUsers = userService.getAllUsers();
// Then
assertNotNull(createdUser.getId());
assertEquals(name, createdUser.getName());
assertEquals(email, createdUser.getEmail());
assertTrue(allUsers.contains(createdUser));
}
}
Mock을 사용한 단위 테스트
@ExtendWith(MockitoExtension.class)
class UserServiceUnitTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
@DisplayName("사용자 생성 시 Repository의 save 메서드가 호출된다")
void testCreateUser() {
// Given
String name = "김개발";
String email = "dev@example.com";
User expectedUser = new User(name, email);
expectedUser.setId(1L);
when(userRepository.save(any(User.class)))
.thenReturn(expectedUser);
// When
User result = userService.createUser(name, email);
// Then
assertEquals(expectedUser.getId(), result.getId());
assertEquals(name, result.getName());
assertEquals(email, result.getEmail());
verify(userRepository, times(1)).save(any(User.class));
}
}
언제 어떤 테스트를 사용할까?
단위 테스트 사용 시기
- 비즈니스 로직 검증
- 예외 상황 테스트
- 빠른 피드백이 필요한 경우
- TDD 개발 시
통합 테스트 사용 시기
- 여러 레이어 간 상호작용 확인
- DB 연동 로직 검증
- API 엔드투엔드 테스트
- 실제 환경과 유사한 테스트 필요 시
단위 테스트 작성 팁
- AAA 패턴 사용 (Arrange, Act, Assert)
- 테스트 메서드 이름을 명확하게
- 하나의 테스트는 하나의 검증만
- 경계값 테스트 필수
통합 테스트 작성 팁
- @Transactional로 롤백 처리
- 테스트용 설정 파일 분리
- 테스트 데이터 초기화 주의
- 실행 시간 고려해서 필요한 것만
테스트 피라미드
/\
/ \ E2E Tests (적음)
/____\
/ \
/ \ Integration Tests (보통)
/__________\
/ \
Unit Tests (많음)
- 단위 테스트: 70%
- 통합 테스트: 20%
- E2E 테스트: 10%
단위 테스트와 통합 테스트는 각각의 목적이 다르다.
둘 다 중요하지만 단위 테스트를 먼저 탄탄히 하고, 필요한 부분에 통합 테스트를 추가하는 것이 좋다.
빠른 피드백과 안정적인 리팩토링을 위해서는 단위 테스트가 핵심이고,
실제 동작 확인을 위해서는 통합 테스트가 필수다.
728x90
반응형