개념
이 레슨의 경계 — 커버리지 도구 사용법은 모듈 53 이 맡는다
구조 커버리지의 개념, 측정 도구 다루기, 문장·분기·조건 커버리지의 기본은 모듈 53 에서 이미 다뤘다. 이 레슨은 그 위에 올라가서 규격이 요구하는 형태의 커버리지 만 본다 — MC/DC 의 정확한 의미, 그것이 증거가 되기 위한 조건, 그리고 100% 에 못 미칠 때의 처리다.
그리고 순서를 먼저 못 박는다. 이 체계에서 구조 커버리지는 시험을 만드는 기준이 아니다. 시험은 요구사항에서 만들고, 구조 커버리지는 그 시험이 코드를 얼마나 실행했는지 사후에 측정 하는 것이다. 이 순서가 뒤집히면 규격이 요구하는 것을 충족하지 못한다. 뒤에서 자세히 본다.
수준에 따라 요구되는 커버리지가 다르다
공개 자료가 설명하는 구도는 이렇다. 구조 커버리지로 쓰이는 기준은 문장 커버리지(statement coverage), 판정 커버리지(decision coverage), MC/DC(modified condition/decision coverage) 세 가지이고, 요구 수준이 높아질수록 더 강한 기준이 요구된다.
- MC/DC 는 가장 높은 수준(A) 에서 요구되는 기준이다
- 판정 커버리지 는 그보다 넓은 범위(A·B) 에 걸린다
- 문장 커버리지 는 더 넓은 범위(A·B·C) 에 걸린다
즉 수준 A 는 세 가지를 모두, B 는 문장과 판정을, C 는 문장을 요구하는 모양이다. 각 수준에 걸리는 커버리지 목표의 정확한 대응과 조항은 규격 원문의 목표 표에서 확인한다(원문 확인). 여기서 중요한 것은 MC/DC 가 추가로 붙는 가장 비싼 요구 라는 점이고, 그래서 수준 A 와 B 의 작업량 차이가 크다는 점이다.
여기에 데이터 결합(data coupling) 과 제어 결합(control coupling) 의 확인 이 별도 요구로 따라붙는다. 모듈 간에 데이터가 실제로 오가고 호출 관계가 실행되었는지를 확인하는 것으로, 모듈 내부의 커버리지와는 다른 축이다. 요구 범위와 수준별 적용은 원문에서 확인한다.
MC/DC 가 요구하는 것 — 독립 효과
MC/DC 의 핵심은 한 문장으로 압축된다. 판정 안의 각 조건이, 그 조건만 바꿨을 때 판정 결과를 바꿀 수 있음을 보인다.
공개 자료가 인용하는 정의의 성격은 이렇다 — 각 조건이 판정의 결과에 독립적으로 영향을 준다는 것 을, 그 조건만 변화시키고 다른 조건은 고정한 상태에서 보인다. 이것을 독립 효과(independent effect) 라 하고, 그것을 보이는 시험 두 개를 독립 쌍(independence pair) 이라 한다.
왜 이 기준이 필요한가. 판정 커버리지만으로는 참 한 번과 거짓 한 번 이면 충족된다. 조건이 세 개인 판정에서 그것은 아주 적은 수의 조합이고, 어떤 조건은 한 번도 결과에 영향을 주지 않은 채 커버리지 100% 가 나올 수 있다. 그 조건이 잘못 쓰였다면 시험은 통과하면서 결함을 통과시킨다.
손으로 해 보는 독립 쌍
조건 세 개인 판정을 보자.
if (A && (B || C)) { ... }
각 행이 하나의 시험 입력이다.
| 번호 | A | B | C | 판정 |
|---|---|---|---|---|
| 1 | F | T | F | F |
| 2 | T | T | F | T |
| 3 | T | F | T | T |
| 4 | T | F | F | F |
이 네 개로 세 조건의 독립 효과가 모두 보인다.
- A 의 독립 쌍 = (1, 2) — B·C 가 고정된 채 A 만 F→T 로 바뀌고 판정이 F→T 로 바뀐다
- B 의 독립 쌍 = (2, 4) — A·C 가 고정된 채 B 만 T→F 로 바뀌고 판정이 T→F 로 바뀐다
- C 의 독립 쌍 = (3, 4) — A·B 가 고정된 채 C 만 T→F 로 바뀌고 판정이 T→F 로 바뀐다
조건 n 개에 대해 시험 n+1 개가 최소 라는 것이 여기서 보인다 — 조건 3 개에 시험 4 개다. 공개 자료가 인용하는 성질이고, 유일원인(unique-cause) 방식에서 성립한다. 이 숫자가 실무에서 갖는 의미는 추정의 근거다. 조건이 10 개인 판정은 최소 11 개의 시험을 요구하고, 그 11 개가 요구사항에서 자연스럽게 나오지 않으면 설계를 들여다봐야 한다는 신호다.
주의 — n+1 은 최소치 이고, 실제로는 조건 조합의 제약(어떤 조합이 물리적으로
불가능한 경우) 때문에 더 늘거나, 최소 집합을 찾기 어려울 수 있다. 그리고 쇼트
서킷 평가(&&, || 가 뒤쪽을 평가하지 않는 것) 가 있는 언어에서는 "고정" 의
의미가 미묘해진다 — 아래에서 다룬다.
유일원인과 마스킹 — 두 형태
위 표에서 쓴 방식은 유일원인(unique-cause) MC/DC 다. 독립 쌍의 두 시험 사이에서 관심 조건과 판정 결과만 바뀌고 나머지 조건은 모두 같다.
문제는 실제 코드에서 이것이 늘 가능하지 않다는 점이다. 같은 조건이 판정 안에 두 번 등장하거나, 쇼트 서킷 평가 때문에 나머지 조건의 값이 정의되지 않을 수 있다.
그래서 마스킹(masking) MC/DC 라는 형태가 있다. 다른 조건들이 판정 결과에
영향을 주지 못하도록 가려진(masked) 상태 라면, 그 조건들의 값이 달라져도
관심 조건의 독립 효과가 성립한다고 본다. 예컨대 A && (B || C) 에서 A 가
거짓이면 B·C 가 무엇이든 판정은 거짓이다 — B·C 는 마스킹된 상태다.
공개 자료가 설명하는 바에 따르면 DO-178C 에서는 MC/DC 의 정의가 완화되어 마스킹 형태가 허용되는 형태로 다뤄진다. 과거에 FAA 가 유일원인, 유일원인+마스킹, 마스킹 세 형태를 검토한 기술 보고서가 공개되어 있고, 그 논의가 배경이다. 허용되는 형태의 정확한 범위와 조건, 그리고 우리 사업에서 어느 형태를 쓸 것인지는 규격 원문과 인증 계획에서 확인한다(원문 확인).
QA 실무에서 이것이 왜 중요한가. 커버리지 도구가 어느 형태로 계산하는지에 따라 같은 시험 묶음의 MC/DC 수치가 달라진다. 도구 설정을 모르고 숫자를 보고하면, 심사에서 "어느 형태인가" 를 물었을 때 답할 수 없다. 그래서 커버리지 보고서에는 도구명·버전·설정·적용한 MC/DC 형태 가 함께 적혀야 한다.
순서를 뒤집지 않는다 — 커버리지는 시험의 목표가 아니다
이것이 이 레슨에서 가장 실무적으로 중요한 부분이다.
옳은 순서
- 요구사항에서 시험 케이스를 만든다(요구사항 기반 시험)
- 그 시험을 실행하고 구조 커버리지를 측정 한다
- 커버리지에 빈 곳이 있으면 왜 비었는지를 분석 한다
틀린 순서
- 코드를 보고 커버리지를 채우는 시험을 만든다
- 커버리지 100% 를 달성한다
왜 틀린가. 두 번째 방식으로 만든 시험은 코드가 틀렸는지 알 수 없다. 코드가 그렇게 쓰여 있으니 그대로 실행한 것이고, 기대 결과도 코드에서 유추한 것이다. 요구사항과 대조되지 않은 시험은 구현을 확인했을 뿐 요구를 확인하지 않았다.
그리고 커버리지의 빈 곳이 갖는 진짜 가치가 사라진다. 요구사항에서 만든 시험으로 덮이지 않은 코드는 네 가지 중 하나를 뜻하는 신호 다.
| 빈 곳의 의미 | 해야 할 일 |
|---|---|
| 시험이 부족하다 | 요구를 다시 읽고 케이스를 보강한다 |
| 요구사항이 누락됐다 | 요구사항을 보완한다(이게 가장 값진 발견이다) |
| 실행될 수 없는 코드(데드 코드) 다 | 제거한다 |
| 의도적으로 실행되지 않는 코드다 | 근거를 문서화한다 |
세 번째와 네 번째의 구분이 중요하다. 실행될 수 없는 코드 는 어떤 입력으로도 도달하지 않는 코드로, 실수의 산물이며 제거 대상이다. 의도적으로 실행되지 않는 코드 는 이 형상에서는 쓰이지 않지만 존재 이유가 있는 코드(예: 특정 옵션에서만 동작하는 부분, 방어적 처리) 로, 제거하지 않고 근거와 격리 근거를 문서화 한다. 용어와 처리 요구의 정확한 정의는 규격 원문에서 확인한다(원문 확인).
QA 가 커버리지 보고서에서 실제로 쓰는 문장은 그래서 "MC/DC 97.4%" 가 아니라 "미달 2.6% 는 항목 14건이고, 그중 9건은 케이스 보강으로 해소, 3건은 요구사항 누락으로 요구를 보완, 2건은 의도적 비활성 코드로 근거를 첨부했다" 다. 숫자만 적힌 보고서는 아무것도 증명하지 않는다.
커버리지 숫자가 증거가 되기 위한 조건
같은 코드와 같은 시험으로도 커버리지 숫자는 달라질 수 있다. 숫자가 증거가 되려면 재현 가능해야 하고, 재현 조건이 보고서에 적혀 있어야 한다.
| 조건 | 왜 숫자를 바꾸는가 |
|---|---|
| 대상 코드의 형상 식별 | 어느 버전을 측정했는가 |
| 컴파일 옵션·최적화 수준 | 최적화가 분기를 합치거나 없앨 수 있다 |
| 계측 방식 | 소스 계측·바이너리 계측에 따라 결과가 다르다 |
| 실행 환경 | 호스트 실행과 대상 하드웨어 실행이 다르다(레슨 4) |
| MC/DC 형태 | 유일원인·마스킹 설정에 따라 달라진다 |
| 집계 범위 | 어느 파일·함수를 분모에 넣었는가 |
여기서 특히 조심할 것이 계측의 영향 이다. 커버리지를 측정하려면 코드에 계측을 넣게 되고, 그러면 코드가 커지고 느려진다. 실시간 제약이 있는 소프트웨어에서는 계측된 코드가 타이밍 요구를 못 맞출 수 있고, 계측 없이 실행한 결과와 계측하고 실행한 결과가 다를 수 있다. 이 문제를 어떻게 처리했는지가 증거의 일부가 된다 — 레슨 4 에서 시험 환경의 조건으로 다시 다룬다.
구조가 복잡하면 커버리지가 비싸진다 — 설계에 되돌리는 피드백
조건이 많은 판정은 MC/DC 비용을 급격히 올린다. 조건 10 개면 최소 11 개의 시험이고, 조합 제약이 있으면 더 늘고, 그 시험들이 요구사항에서 정당화되어야 한다.
그래서 이 분야에서는 커버리지 요구가 설계에 되돌아온다. 복잡한 조건식을 의미 있는 단위로 쪼개면 각각의 요구사항 근거가 분명해지고 시험 수가 줄어든다. QA 가 코드 검토에서 "이 판정은 조건이 많아 MC/DC 비용이 큽니다" 라고 지적하는 것은 스타일 지적이 아니라 일정과 증거에 대한 지적 이다. 이것이 이 분야 QA 가 설계 단계에서 발언하는 근거가 된다.
실무에서 왜 필요한가
MC/DC 는 이 분야 공고와 면접에 거의 항상 등장하는 단어이고, 얕게 아는 사람과 실제로 해 본 사람이 바로 갈린다. 얕게 아는 사람은 "조건 조합을 다 보는 것" 이라고 답한다(그건 조건 조합 커버리지이고 MC/DC 가 아니다). 해 본 사람은 독립 쌍을 손으로 그려 보이고, n+1 을 말하고, 도구 설정에 따라 숫자가 달라진다는 것을 안다.
실무에서 이 레슨의 내용이 직접 돈이 되는 지점은 셋이다.
1) 작업량 추정. "이 모듈은 수준 A 입니다" 라는 말에서 MC/DC 비용을 떼어 추정할 수 있으면 일정 협의에서 근거를 갖는다. 조건식의 복잡도를 훑어 보고 "이 설계로는 시험 수가 몇 배가 됩니다" 를 말할 수 있는 것이 그 근거다.
2) 미달 처리. 커버리지 100% 가 안 나오는 상황은 정상이다. 그때 분류하고 근거를 붙이는 절차 를 아는 사람이 사업을 끌고 간다. 모르는 팀은 100% 를 맞추려 의미 없는 시험을 만들거나, 미달을 숨긴 채 넘어가 심사에서 되돌아온다.
3) 요구사항 누락 발견. 커버리지의 빈 곳에서 요구사항 누락을 찾아내는 것은 이 분야 QA 가 만드는 가장 값진 결과물이다. 요구가 없는데 코드가 있는 상황은 안전이 걸린 소프트웨어에서 심각한 신호이고, 그것을 발견하는 통로가 구조 커버리지다.