개념
왜 일부 경로만 테스트하는가
가장 이상적인 구조 기반 테스트는 모든 경로 를 한 번씩 실행하는 것이다. 하지만 반복문 안에 분기가 하나만 있어도 반복 횟수 n에 따라 경로가 2^n 개로 늘어난다(n = 20이면 백만 개 이상). 그래서 문장·결정·조건 같은 기준 을 정해 경로의 일부만 고른다. 이 기준이 커버리지다.
제어 흐름 그래프와 기본 블록
- 노드 — 기본 블록(중간에 분기가 없이 한 번에 실행되는 문장 묶음) 또는 결정
- 간선 — 실행 흐름
- 결정 노드 — 나가는 간선이 2개 이상인 노드
결정과 조건 — DO-178의 정의
| 용어 | 정의 | 예 if (x > 0 && y <= -3) |
|---|---|---|
| 조건(Condition) | 논리 연산자(&&, ||)를 포함하지 않는 불리언 식 | x > 0, y <= -3 각각 |
| 결정(Decision) | 조건들이 0개 이상의 논리 연산자로 묶인 불리언 식 | x > 0 && y <= -3 전체 |
조건이 하나뿐인 결정은 그 자체가 조건이다. 같은 조건이 결정에 두 번 나오면 별개의 조건 으로
센다. 가이드는 switch 처럼 참·거짓이 아닌 여러 값을 갖는 결정도 결정으로 다룬다.
여섯 가지 커버리지
| 커버리지 | 달성 조건 | 비율 계산 |
|---|---|---|
| 문장(Statement) | 실행 가능한 모든 문장을 1회 이상 | 실행한 문장 ÷ 전체 문장 |
| 결정(Decision, = 분기) | 모든 결정의 결과 가 참·거짓을 각각 1회 이상 | 실행한 결정 결과 ÷ (결정 수 × 2) |
| 조건(Condition) | 모든 조건 이 참·거짓을 각각 1회 이상 | 나온 조건 값 ÷ (조건 수 × 2) |
| 결정/조건(Decision/Condition) | 결정 커버리지 + 조건 커버리지를 동시에 | |
| 다중 조건(Multiple Condition) | 한 결정 안 조건 값의 모든 조합 | 조건 n개면 2^n |
| MC/DC(변형 조건/결정) | 결정/조건 + 각 조건이 다른 조건을 고정한 채 단독으로 결정 결과를 바꿈 을 보임 | 보통 n + 1개 |
포함 관계(강한 쪽이 약한 쪽을 보장)는 다음과 같다.
다중 조건 ⇒ MC/DC ⇒ 결정/조건 ⇒ 결정 ⇒ 문장
⇓
조건
조건 커버리지는 결정 커버리지를 보장하지 않는다. 이것이 가장 자주 나오는 함정이다. 반대로 결정 커버리지도 조건 커버리지를 보장하지 않는다. 둘을 함께 요구하는 것이 결정/조건이다.
한 함수로 여섯 가지를 모두 계산하기
void f(int x, int y) {
if (x > 0 && y > 0) // 결정 D, 조건 A: x > 0, 조건 B: y > 0
s1;
s2;
}| 커버리지 | 최소 TC | 예 (x, y) → (A, B) → D | 개수 |
|---|---|---|---|
| 문장 | D가 참이 되는 한 개면 s1·s2 모두 실행 | (1, 1) → (T, T) → T | 1 |
| 결정 | D 참 1개, 거짓 1개 | (1, 1) → T / (0, 1) → F | 2 |
| 조건 | A·B가 각각 T·F | (1, 0) → (T, F) → F / (0, 1) → (F, T) → F | 2 |
| 결정/조건 | 위 둘을 동시에 | (1, 1) → (T, T) → T / (0, 0) → (F, F) → F | 2 |
| 다중 조건 | 모든 조합 TT·TF·FT·FF | 4개 | 4 |
| MC/DC | A의 단독 영향 (TT vs FT), B의 단독 영향 (TT vs TF) | (T, T), (F, T), (T, F) | 3 |
조건 커버리지 행을 보자. 두 TC 모두 결정이 거짓 이라 s1이 한 번도 실행되지 않는다. 조건 커버리지 100%인데 결정 커버리지 50%, 문장 커버리지도 100%가 아니다. 시험은 이 표를 그대로 묻는다.
단축 평가 주의 — C·Java의 && 는 A가 거짓이면 B를 평가하지 않는다. 실행 기준으로 보면 (0, 1)
에서 B는 평가되지 않으므로 B의 T가 "나왔다"고 보기 어렵다. 가이드 예제는 대개 값 기준(진리값)
으로 계산하지만, 문제에 "단축 평가를 고려하여"라는 말이 있으면 평가된 조건만 센다.
MC/DC는 왜 n + 1인가
조건 n개짜리 결정에서 각 조건마다 "그 조건만 바꿔 결과가 뒤집히는 TC 쌍"이 필요하다. 쌍을 서로 공유하면 n + 1개로 끝난다.
- AND(
&&) n개 — 전부 T인 1개 + 각 조건 하나만 F인 n개 = n + 1 - OR(
||) n개 — 전부 F인 1개 + 각 조건 하나만 T인 n개 = n + 1
예: A || B || C 의 MC/DC 집합은 (F,F,F), (T,F,F), (F,T,F), (F,F,T) 4개다. 다중 조건은
2^3 = 8개다. 조건이 많을수록 MC/DC가 다중 조건보다 압도적으로 싸다 — 그래서 항공(DO-178C
Level A)·자동차(ISO 26262 ASIL D)가 MC/DC를 요구한다.
결정이 여러 개일 때의 결정 커버리지
결정이 순차로 여러 개면 TC 하나가 여러 결정의 결과를 동시에 덮을 수 있다.
if (a > 0) s1; // D1
if (b > 0) s2; // D2(a, b) = (1, 1)과 (0, 0) 두 개로 D1·D2 모두 참·거짓을 덮는다 — 결정 수가 2라고 TC가 4개 필요한 것이 아니다. 최소 TC 수는 결정 하나가 갖는 결과의 최대 개수 와 같거나 그보다 크다. 결정이 중첩 되어 있으면 다르다.
if (a > 0) { // D1
if (b > 0) s1; // D2 — D1 이 참일 때만 도달
else s2;
} else s3;D2의 참·거짓은 D1이 참인 경로에서만 나오므로 (1, 1), (1, 0), (0, 임의) 세 개가 필요하다.
경로가 주어졌을 때 커버리지 계산
단답형은 제어 흐름 그래프와 실행 경로를 주고 비율을 묻는다. 예시 그래프(노드 = 기본 블록):
1
│
2 ──(F)──▶ 4
│(T) │
3 │
│ │
5 ◀────────┘
│
6 ──(F)──▶ 8
│(T) │
7 │
│ │
9 ◀────────┘
결정은 2와 6 두 개, 결정 결과는 4개(2T·2F·6T·6F), 노드는 9개다.
- 경로
1-2-3-5-6-8-9하나를 실행하면 — 노드 7개/9개 실행(4·7 미실행) → 문장 7/9 ≈ 77.8%, 결정 결과 2T·6F 2개/4개 → 결정 50% - 경로 하나를 더해
1-2-4-5-6-7-9를 실행하면 → 문장 9/9 = 100%, 결정 4/4 = 100% - 결정 100%를 위한 최소 TC는 2 다. 순환 복잡도는 분기 노드 2 + 1 = 3 이므로 기본 경로
테스트라면 3개(예:
T,T,F,F,T,F)가 필요하다
기본 경로 테스트
순환 복잡도 V(G)만큼의 선형 독립 경로 를 고르고 각 경로를 실행하는 TC를 만든다. 기본 경로 테스트는 결정 커버리지를 보장한다. 루프가 있으면 "0회·1회 반복"이 서로 다른 독립 경로가 된다.
실무에서 왜 필요한가
커버리지 도구(JaCoCo·Istanbul·gcov)는 숫자를 주지만 어떤 기준의 숫자인지 는 사람이 읽어야 한다. JaCoCo의 "Branches"는 결정 커버리지에 가깝고, 조건 단위 정보는 일부만 보여 준다. 그래서 "분기 85%" 리포트를 보고 "조건 하나가 항상 참으로만 평가된 결정이 남아 있을 수 있다"고 질문할 수 있어야 한다 (s3-m53-l01). 안전 필수 프로젝트에서는 MC/DC 증거를 감사에 내야 하는데, TC 쌍을 직접 설명할 수 있어야 감사 질의를 넘긴다(s2-m35 모듈).