반응형
테스트 커버리지란?
- 정의: 전체 코드 중 테스트가 실행된 코드의 비율
- 측정 단위: 퍼센트(%)로 표현
- 목적: 코드 품질과 안정성 확보
적정 테스트 커버리지 수치
일반적인 권장사항
- 최소 기준: 70% 이상
- 권장 수준: 80-90%
- 100%는 현실적이지 않음 (비용 대비 효과 낮음)
프로젝트별 기준
- 금융/의료 시스템: 90% 이상
- 일반 웹 애플리케이션: 80% 정도
- 스타트업/MVP: 60-70%
커버리지 유형별 이해
Line Coverage (라인 커버리지)
public class Calculator {
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero"); // 이 라인이 실행되었나?
}
return a / b; // 이 라인이 실행되었나?
}
}
Branch Coverage (분기 커버리지)
// 모든 if-else 경로를 테스트했는가?
@Test
void testDivideByZero() {
assertThrows(IllegalArgumentException.class,
() -> calculator.divide(10, 0)); // 예외 경로 테스트
}
@Test
void testNormalDivision() {
assertEquals(5, calculator.divide(10, 2)); // 정상 경로 테스트
}
Java에서 커버리지 측정하기
JaCoCo 설정 (Maven)
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
JaCoCo 설정 (Gradle)
plugins {
id 'jacoco'
}
jacoco {
toolVersion = "0.8.8"
}
jacocoTestReport {
reports {
xml.required = false
csv.required = false
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}
효과적인 테스트 작성 방법
1. 핵심 비즈니스 로직 우선
@Service
public class OrderService {
// 이런 핵심 로직은 반드시 테스트!
public Order processOrder(OrderRequest request) {
validateOrder(request);
calculatePrice(request);
return saveOrder(request);
}
@Test
void shouldProcessValidOrder() {
// Given
OrderRequest request = new OrderRequest("product1", 2);
// When
Order result = orderService.processOrder(request);
// Then
assertThat(result.getStatus()).isEqualTo(OrderStatus.COMPLETED);
}
}
2. 경계값 테스트
@Test
void testAgeValidation() {
// 경계값들을 모두 테스트
assertThrows(ValidationException.class, () -> validator.validate(17)); // 미성년자
assertDoesNotThrow(() -> validator.validate(18)); // 경계값
assertDoesNotThrow(() -> validator.validate(65)); // 정상값
}
3. 예외 상황 테스트
@Test
void shouldHandleEmptyDatabase() {
// Given
when(userRepository.findAll()).thenReturn(Collections.emptyList());
// When & Then
assertThrows(NoDataException.class,
() -> userService.getAllUsers());
}
커버리지 함정 주의사항
높은 수치 ≠ 좋은 테스트
// 나쁜 예: 커버리지만 높이는 테스트
@Test
void badTest() {
userService.createUser("test");
userService.deleteUser(1L);
userService.updateUser(1L, "new");
// 결과 검증 없음 - 의미없는 테스트
}
// 좋은 예: 의미있는 검증이 포함된 테스트
@Test
void shouldCreateUserSuccessfully() {
// Given
String username = "testUser";
// When
User result = userService.createUser(username);
// Then
assertThat(result.getUsername()).isEqualTo(username);
assertThat(result.getId()).isNotNull();
}
팀별 커버리지 전략
개발 단계별 접근
- 초기 개발: 핵심 로직 60%
- 기능 완성: 전체 80%
- 운영 준비: 중요 부분 90%
코드별 우선순위
- 비즈니스 로직: 90% 이상
- 유틸리티 클래스: 80% 이상
- 컨트롤러: 70% 이상
- 설정 클래스: 50% 정도
실무에서 자주 쓰는 도구들
1. IntelliJ IDEA 커버리지
- Run with Coverage 기능 활용
- 실시간으로 커버되지 않은 라인 확인 가능
2. SonarQube 연동
# GitHub Actions 예시
- name: SonarQube Scan
run: ./gradlew sonarqube
3. 커버리지 품질 게이트
<!-- 80% 미만시 빌드 실패 -->
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
기억할 포인트
- 수치보다 품질이 중요
- 핵심 로직 먼저, 전체 커버리지는 나중에
- 팀 상황에 맞는 현실적인 목표 설정
- 지속적인 측정과 개선
체크리스트
- [ ] 내가 작성한 코드의 핵심 로직은 테스트했는가?
- [ ] 예외 상황도 고려했는가?
- [ ] 테스트가 실제로 의미있는 검증을 하는가?
- [ ] 커버리지 도구를 정기적으로 확인하는가?
테스트 커버리지는 목표가 아닌 수단
안정적이고 유지보수 가능한 코드를 만드는 것이 진짜 목표
728x90
반응형
'코딩:개발일지' 카테고리의 다른 글
API 문서 작성 도구 선택 가이드 (Swagger, Redoc 등) (0) | 2025.06.04 |
---|---|
코드 리뷰에서 자주 지적받는 실수는 무엇일까 (0) | 2025.05.31 |
단위 테스트(Unit Test) vs 통합 테스트(Integration Test) 차이점 정리 (1) | 2025.05.28 |
Redis는 어떤 상황에서 사용하는 게 좋을까? (0) | 2025.05.27 |
JWT vs OAuth2 정리 - 백엔드 개발자가 알아야 할 개념 (1) | 2025.05.25 |