구문 커버리지 (Statement Coverage)#
구문 커버리지는 프로그램을 구성하는 모든 문장들이 최소한 한 번은 실행될 수 있는 입력 데이터를 테스트 데이터로 선정하는 기준이다.
또한 라인 커버리지(Line Coverage)라고도 불린다.
먼저 간단한 예제를 통해 구문 커버리지의 이해:
1
2
3
4
5
6
7
8
9
10
11
12
13
| def calculate_grade(score):
# 구문 1
if score >= 90:
# 구문 2
grade = 'A'
elif score >= 80:
# 구문 3
grade = 'B'
else:
# 구문 4
grade = 'C'
# 구문 5
return grade
|
이 함수의 모든 구문을 실행하기 위해서는 다음과 같은 테스트 케이스가 필요하다:
1
2
3
4
5
6
7
8
9
| def test_calculate_grade():
# 구문 1, 2, 5를 실행
assert calculate_grade(95) == 'A'
# 구문 1, 3, 5를 실행
assert calculate_grade(85) == 'B'
# 구문 1, 4, 5를 실행
assert calculate_grade(75) == 'C'
|
여기서 각 테스트 케이스가 실행하는 구문을 추적하면서 커버리지를 계산할 수 있다.
이 예제에서는 모든 구문(1-5)이 최소 한 번 이상 실행되므로 100% 구문 커버리지를 달성함.
이제 더 복잡한 실제 예제를 통해 구문 커버리지의 중요성을 살펴보자:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| public class BankAccount {
private double balance;
private boolean frozen;
public boolean withdraw(double amount) {
// 구문 1
if (amount <= 0) {
// 구문 2
return false;
}
// 구문 3
if (frozen) {
// 구문 4
return false;
}
// 구문 5
if (balance >= amount) {
// 구문 6
balance -= amount;
// 구문 7
return true;
}
// 구문 8
return false;
}
}
|
이 은행 계좌 시스템의 모든 구문을 테스트하기 위한 테스트 케이스를 작성해보면:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| @Test
void testWithdraw() {
BankAccount account = new BankAccount();
// 음수 금액 테스트 (구문 1, 2)
assertFalse(account.withdraw(-100));
// 계좌 동결 상태 테스트 (구문 1, 3, 4)
account.setFrozen(true);
assertFalse(account.withdraw(50));
// 정상 출금 테스트 (구문 1, 3, 5, 6, 7)
account.setFrozen(false);
account.deposit(100);
assertTrue(account.withdraw(50));
// 잔액 부족 테스트 (구문 1, 3, 5, 8)
assertFalse(account.withdraw(1000));
}
|
구문 커버리지의 계산 방법은 다음과 같다:
1
2
3
4
5
6
| 구문 커버리지 = (실행된 구문의 수) / (전체 구문의 수) × 100%
예를 들어, 위의 BankAccount 예제에서:
- 전체 구문 수: 8
- 실행된 구문 수: 8
따라서, 구문 커버리지 = (8/8) × 100% = 100%
|
구문 커버리지를 측정할 때 주의해야 할 점점#
예외 처리가 포함된 코드:
1
2
3
4
5
6
7
8
9
10
11
| public double divideSafely(int numerator, int denominator) {
try {
// 구문 1
return numerator / denominator;
} catch (ArithmeticException e) {
// 구문 2
System.err.println("Division by zero");
// 구문 3
return 0;
}
}
|
이 경우 정상 실행과 예외 발생 상황 모두를 테스트해야 한다.
조건부 실행이 있는 코드:
1
2
3
4
5
6
7
8
9
| public void processTransaction(boolean debug) {
// 구문 1
performTransaction();
if (debug) {
// 구문 2
logDebugInfo();
}
}
|
디버그 모드가 켜진 경우와 꺼진 경우 모두를 테스트해야 한다.
측정 방법#
구문 커버리지는 다음과 같은 공식으로 계산된다:
구문 커버리지(%) = (실행된 구문의 수 / 전체 구문의 수) × 100
- 코드의 모든 구문을 실행할 수 있는 입력값이나 이벤트 등의 테스트 데이터를 제공하면 달성된다.
- 가장 기본적인 커버리지 측정 방법으로, 다른 커버리지 기법들에 비해 측정 강도가 가장 약하다.
- 분기 커버리지, 다중 조건 커버리지, 경로 커버리지 등 포함관계가 더 큰 커버리지를 달성하면 저절로 달성된다.
- 적은 개수의 테스트 데이터로 쉽게 달성할 수 있다.
- 테스트 진행 정도를 코드의 범위 형태로 표현하기 때문에, 개발자가 커버리지의 의미를 직관적으로 이해할 수 있다.
한계점#
- 코드 상에 존재하는 가능한 경우 중 많은 부분을 검증하지 못하는 보장성이 낮은 커버리지이다.
- 조건문의 모든 경우를 테스트하지 못할 수 있다. 예를 들어, if문의 조건이 참인 경우만 테스트되고 거짓인 경우는 테스트되지 않을 수 있다.
그래서 구문 커버리지는 보통 다른 커버리지 지표들(분기 커버리지, 조건 커버리지 등)과 함께 사용된다.
예를 들어, 다음과 같은 테스트 전략을 수립할 수 있다:
- 기본적인 구문 커버리지로 시작하여 실행되지 않는 코드를 찾는다.
- 분기 커버리지를 통해 조건문의 다양한 경로를 테스트한다.
- 필요한 경우 더 높은 수준의 커버리지(조건, MC/DC 등)를 적용한다.
이러한 체계적인 접근을 통해 소프트웨어의 품질을 효과적으로 보장할 수 있다.
참고 및 출처#