조건 커버리지 (Condition Coverage)#
조건 커버리지는 결정 포인트 내의 각 개별 조건식이 참(true)과 거짓(false)의 결과를 최소한 한 번씩 갖도록 테스트하는 기법이다.
이는 전체 조건식의 결과와는 독립적으로 각 개별 조건의 결과에 초점을 맞춘다.
주요 특징#
- 개별 조건 중심: 전체 조건식이 아닌 각 개별 조건식의 결과를 검증한다.
- 최소 요구사항: 각 조건이 최소한 한 번씩 참과 거짓의 결과를 가져야 한다.
- 세분화된 테스트: 복잡한 조건문의 각 부분을 개별적으로 테스트할 수 있다.
- 조건의 독립적 평가: 각 조건을 독립적으로 평가하여 더 세밀한 테스트가 가능하다.
- 제어 흐름에 대한 높은 민감도: 프로그램의 제어 흐름을 더 정확하게 테스트할 수 있다.
- 결정 커버리지보다 강력: 더 많은 테스트 케이스를 요구하므로 더 철저한 테스트가 가능하다.
- 전체 조건식 결과 보장 부족: 개별 조건의 참/거짓만을 테스트하므로 전체 조건식의 모든 결과를 보장하지 않을 수 있다.
- 테스트 케이스 증가: 조건의 수가 많아질수록 필요한 테스트 케이스의 수가 증가한다.
따라서 조건 커버리지는 다른 테스트 커버리지 지표들(구문 커버리지, 분기 커버리지 등)과 함께 사용되어야 하며, 이를 통해 더 완성도 높은 테스트를 수행할 수 있다.
조건 커버리지를 계산하는 방법#
1
2
3
4
5
6
| public boolean isEligibleForDiscount(int age, boolean isMember, int purchaseAmount) {
if (age >= 60 && isMember || purchaseAmount > 1000) {
return true;
}
return false;
}
|
이 코드에는 세 가지 개별 조건이 있다:
- age >= 60
- isMember
- purchaseAmount > 1000
완전한 조건 커버리지를 달성하기 위해서는, 각 조건이 독립적으로 참과 거짓을 모두 가져야 한다.
이를 위한 테스트 케이스를 설계해보면,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| @Test
void testDiscountEligibility() {
// age >= 60 조건 테스트
assertTrue(isEligibleForDiscount(65, true, 500)); // age: true
assertFalse(isEligibleForDiscount(55, true, 500)); // age: false
// isMember 조건 테스트
assertTrue(isEligibleForDiscount(65, true, 500)); // isMember: true
assertFalse(isEligibleForDiscount(65, false, 500)); // isMember: false
// purchaseAmount > 1000 조건 테스트
assertTrue(isEligibleForDiscount(55, false, 1500)); // purchaseAmount: true
assertFalse(isEligibleForDiscount(55, false, 800)); // purchaseAmount: false
}
|
1
2
3
4
5
6
7
8
9
| 조건 커버리지 = (테스트된 조건 결과의 수) / (전체 가능한 조건 결과의 수) × 100%
예시에서:
- 전체 조건 수: 3개
- 각 조건당 가능한 결과: 2개 (참/거짓)
- 전체 가능한 조건 결과의 수: 3 × 2 = 6
- 테스트된 조건 결과의 수: 6
따라서, 조건 커버리지 = (6/6) × 100% = 100%
|
조건 커버리지의 한계#
1
2
3
4
5
6
| public boolean validateUser(String username, String password, boolean isActive) {
if (username != null && password != null && isActive) {
return true;
}
return false;
}
|
이 코드에서 각 조건을 독립적으로 테스트하더라도, 조건들의 모든 가능한 조합을 커버하지는 못할 수 있다.
예를 들어:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 조건 커버리지는 만족하지만, 모든 시나리오를 커버하지는 못하는 테스트
@Test
void testUserValidation() {
// username != null 테스트
assertTrue(validateUser("user", "pass", true)); // true
assertFalse(validateUser(null, "pass", true)); // false
// password != null 테스트
assertTrue(validateUser("user", "pass", true)); // true
assertFalse(validateUser("user", null, true)); // false
// isActive 테스트
assertTrue(validateUser("user", "pass", true)); // true
assertFalse(validateUser("user", "pass", false)); // false
}
|
조건 커버리지를 향상시키기 위한 실제적인 접근 방법#
테스트 자동화 도구 활용:
1
2
3
4
5
6
| // JaCoCo와 같은 테스트 커버리지 도구 사용
@Test
@CoverageTarget(type = CoverageType.CONDITION)
void comprehensiveTest() {
// 테스트 케이스들
}
|
경계값 분석과 결합:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public boolean validateAge(int age, boolean hasParentalConsent) {
if (age >= 13 && age <= 19 && hasParentalConsent) {
return true;
}
return false;
}
@Test
void testAgeValidation() {
// 경계값과 조건 커버리지를 결합한 테스트
assertFalse(validateAge(12, true)); // 경계값 미만
assertTrue(validateAge(13, true)); // 최소 경계값
assertTrue(validateAge(16, true)); // 중간값
assertTrue(validateAge(19, true)); // 최대 경계값
assertFalse(validateAge(20, true)); // 경계값 초과
assertFalse(validateAge(15, false)); // 동의 없음
}
|
다음과 같은 조건문이 있다고 가정해 보자:
1
2
3
| if (a > 0 && b < 10) {
// 코드 실행
}
|
조건 커버리지를 만족시키기 위해서는 다음과 같은 테스트 케이스가 필요하다:
- a > 0 (참), b < 10 (참)
- a > 0 (참), b < 10 (거짓)
- a > 0 (거짓), b < 10 (참)
- a > 0 (거짓), b < 10 (거짓)
이렇게 각 개별 조건이 참과 거짓의 결과를 모두 가지도록 테스트 케이스를 구성한다.
개별 조건식이 참/거짓을 모두 가지는 비율
예를 들어, (A && B)라는 조건에서 A와 B 각각에 대해 true/false 케이스를 테스트한다.
1
2
3
4
5
6
7
8
9
10
11
12
| def is_eligible_for_discount(age, is_student, purchase_amount):
if (age < 18 or is_student) and purchase_amount >= 100:
return True
return False
# 테스트 코드
def test_discount_eligibility():
# 모든 조건 조합 테스트
assert is_eligible_for_discount(16, False, 150) # 미성년자
assert is_eligible_for_discount(25, True, 150) # 학생
assert not is_eligible_for_discount(25, False, 150) # 성인, 비학생
assert not is_eligible_for_discount(16, True, 50) # 구매액 부족
|
참고 및 출처#