티스토리 뷰

반응형
소프트웨어 테스트 커버리지: 개념, 코드, 테스트케이스별 퍼센트까지 완전 정리

🔷 소프트웨어 테스트 커버리지: 개념, 코드, 테스트케이스별 퍼센트까지 완전 정리


📖 커버리지란 무엇인가요?

커버리지(coverage)는 소프트웨어 테스트에서 “코드의 어느 부분이 실제 테스트 되었는지를 수치로 표현한 비율”입니다.

소프트웨어는 작성된 코드 전체가 실제 실행되거나 검증된다는 보장이 없습니다. 많은 경우 테스트 케이스가 충분하지 않으면 특정 조건, 특정 분기, 특정 경로가 전혀 실행되지 않아도 프로그램은 오류 없이 통과해 보이지만, 실제 상황에서는 숨겨진 오류가 남아 있을 수 있습니다. 이런 문제를 막기 위해 커버리지라는 개념이 등장했습니다.


🔷 커버리지를 측정하는 이유

  • 테스트가 코드의 얼마나 많은 부분을 다루고 있는지 확인
  • 테스트되지 않은 사각지대를 찾기 위해
  • 소프트웨어의 품질과 안정성을 높이기 위해

🔷 커버리지의 기본 계산법

테스트가 코드의 특정 항목을 몇 개 실행했는지를 전체 항목 수로 나누어 백분율로 나타냅니다.

\[ \text{커버리지 (\%)} = \frac{\text{테스트된 항목 수}}{\text{전체 항목 수}} \times 100 \]

예를 들어, 100개의 코드 라인 중 80개가 실행되었다면:

구문 커버리지 = 80%

🔷 커버리지의 한계

커버리지가 100%라고 해서 버그가 없다는 뜻은 아닙니다. 단지 코드가 실행되었다는 뜻일 뿐, 실행된 코드가 올바른 결과를 내는지를 검증하는 것은 아닙니다.
또한 커버리지에는 여러 기준이 있어 단순히 라인만 보는 것이 아니라, 조건의 참/거짓, 분기, 실행 경로 등 다양한 관점에서 측정할 수 있습니다.


이제부터는 각 커버리지 기법별로 개념, 코드 예제, 그리고 테스트 케이스별로 어떻게 진행되면 좋은지, 어떤 퍼센트를 가지는지를 하나씩 설명하겠습니다.
각각의 기준을 명확히 이해하고, 실제 코드에 어떻게 적용되는지를 확인하면 테스트 설계가 훨씬 체계적으로 바뀔 수 있습니다.


📝 구문 커버리지 (Statement Coverage)

정의
구문 커버리지는 프로그램의 모든 문장이 최소 한 번 이상 실행되었는지를 확인하는 기법입니다.

테스트가 작성된 코드의 모든 문장을 한 번이라도 실행해 보는지 평가하는 가장 기본적인 수준의 테스트입니다.
코드의 어떤 부분도 실행되지 않은 채 남아 있다면, 그 부분에는 잠재적인 오류가 있을 수 있습니다.
구문 커버리지는 반드시 달성해야 할 최소 목표이며, 더 높은 수준의 테스트를 하기 전 필수적으로 확보합니다.

💡 구문 커버리지가 100%여도 프로그램의 논리적 정확성을 완전히 보장하지는 않습니다.

👨‍💻 코드 예제


int main() {
    int a = 15;
    if (a > 10) {
        // 문장 1: a가 10보다 크면 실행
    } else {
        // 문장 2: a가 10 이하이면 실행
    }
    return 0;
}
    

🔷 테스트케이스별 해설

이 프로그램에는 총 4개의 문장(statement)이 있습니다:
1️⃣ int a = 15; → 항상 실행됨
2️⃣ if (a > 10) → 항상 평가됨
3️⃣ 문장 1 (if 블록)
4️⃣ 문장 2 (else 블록)

단일 테스트케이스로는 if문 또는 else문 중 하나만 실행됩니다.
즉, 한 케이스만으로는 3개만 실행되고 나머지 1개는 실행되지 않으므로 구문 커버리지는 75%입니다.
두 케이스를 모두 수행해 두 문장이 모두 실행되면 100%를 달성합니다.


📊 케이스별 실행 흐름 요약

테스트케이스 입력값 a 실행된 문장 전체 문장 구문 커버리지
TC1 15 1️⃣ 2️⃣ 3️⃣ 4개 중 3개 75%
TC2 5 1️⃣ 2️⃣ 4️⃣ 4개 중 3개 75%
TC1 + TC2 15, 5 1️⃣ 2️⃣ 3️⃣ 4️⃣ 4개 중 4개 100%

📌 특징

✅ 장점

  • 테스트가 쉽고 빠르며 직관적입니다.
  • 코드상의 죽은 코드(unreachable code)를 발견할 수 있습니다.

❌ 단점

  • 조건문에서 조건별 논리적 오류는 발견하지 못합니다.
  • 참/거짓 조건별 동작이 모두 검증되었다고 보장할 수 없습니다.

📝 결정 커버리지 (Decision Coverage)

정의
결정 커버리지는 프로그램의 모든 분기(if/else)의 결과가 참과 거짓 모두 실행되었는지를 확인하는 기법입니다.

구문 커버리지가 단순히 코드 라인의 실행 여부를 확인하는 것이라면, 결정 커버리지는 조건문의 결정(참/거짓) 결과에 따른 흐름을 모두 확인하는 것이 목표입니다.
코드가 특정 분기로만 실행되는 경우, 다른 방향으로 동작했을 때 오류가 발생할 수 있으므로, 결정 커버리지를 통해 이를 방지합니다.

💡 결정 커버리지가 100%라면 모든 분기의 참/거짓 결과가 적어도 한 번씩은 테스트되었다는 뜻입니다.

👨‍💻 코드 예제


int main() {
    int a = 8;
    if (a > 10) {
        // 분기: 참
    } else {
        // 분기: 거짓
    }
    return 0;
}
    

🔷 테스트케이스별 해설

이 프로그램의 핵심은 if (a > 10)의 조건입니다.
조건이 참일 때와 거짓일 때 각각의 분기가 모두 실행되도록 테스트해야 결정 커버리지가 100%가 됩니다.

총 문장은 4개(선언, if, 참 분기, 거짓 분기)이지만, 결정 커버리지의 초점은 참/거짓 두 흐름을 모두 확인하는 것입니다.
단일 테스트케이스로는 한 방향의 흐름만 확인되므로, 반드시 두 가지 테스트케이스가 필요합니다.


📊 케이스별 실행 흐름 요약

테스트케이스 입력값 a 실행된 분기 결정 커버리지
TC1 12 참 분기 50%
TC2 8 거짓 분기 50%
TC1 + TC2 12, 8 참/거짓 모두 실행 100%

📌 특징

✅ 장점

  • 코드의 흐름을 조건의 참/거짓까지 모두 테스트할 수 있습니다.

❌ 단점

  • 조건식 내부의 개별 조건까지는 검증하지 못합니다.
  • 단순 참/거짓 흐름 외의 논리적 오류는 여전히 놓칠 수 있습니다.

📝 조건 커버리지 (Condition Coverage)

정의
조건 커버리지는 프로그램 내 각 조건식이 참/거짓의 값을 최소 한 번씩 가져보았는지를 확인하는 기법입니다.
예를 들어, if (a>10 && b<5)라는 조건이 있다면, a>10b<5 각각이 참과 거짓을 모두 경험했는지를 테스트합니다.

👨‍💻 코드 예제


int main() {
    int a = 12, b = 3;
    if (a > 10 && b < 5) {
        // 문장 1
    } else {
        // 문장 2
    }
    return 0;
}
    

조건은 2개입니다:

  • a>10 → 참/거짓
  • b<5 → 참/거짓

총 4가지 상태가 필요합니다:
a=T, b=T, a=T, b=F, a=F, b=T, a=F, b=F


📊 테스트케이스별 조건 커버리지 평가

테스트케이스 입력값 (a,b) 확인된 상태 조건 커버리지
TC1 (12,3) a=T, b=T 50%
TC2 (12,7) a=T, b=F 50%
TC3 (8,3) a=F, b=T 50%
TC4 (8,7) a=F, b=F 50%

✅ 각 테스트케이스는 독립적이므로, 각각 50%로 평가됩니다.


📊 전체 테스트 계획의 진행에 따른 커버리지

단계 포함된 테스트케이스 확인된 상태 전체 조건 커버리지
1 TC1 a=T, b=T 50%
2 TC1 + TC2 a=T, b=T/F 75%
3 TC1 + TC2 + TC3 a=T/F, b=T/F 100%

📌 특징

✅ 장점

  • 각 조건이 실제로 참/거짓 모두를 경험했는지를 확인 가능
  • 논리 오류를 발견하기에 효과적

❌ 단점

  • 조건 간의 상호작용을 검증하지는 못함
  • 테스트케이스가 많아질 수 있음

📝 변경 조건/결정 커버리지(MC/DC)

🔷 개념 설명

변경 조건/결정 커버리지(MC/DC)는 조건문 안에 여러 조건이 동시에 있을 때 사용하는 테스트 방법입니다.

예를 들어 조건문이 다음과 같다고 생각해 보겠습니다.


if (조건 A && 조건 B) {
    // 참일 때 실행
} else {
    // 거짓일 때 실행
}
    

위와 같은 조건문은 두 가지 조건이 함께 묶여서 결과가 결정됩니다.

이때 MC/DC가 하는 일은 각 조건이 조건문의 전체 결과에 혼자서 영향을 줄 수 있는지 확인하는 것입니다.
즉, 각 조건이 독립적인지 알아보려면, 다른 조건은 그대로 유지한 채 하나의 조건만 바꿨을 때, 전체 조건문의 결과가 바뀌는지를 보는 방식입니다.


🔷 코드 예시를 통한 이해

아래와 같은 코드가 있습니다.


int main() {
    int a = 6, b = 7;
    if (a > 5 && b < 10) {
        // 참일 때 실행
    } else {
        // 거짓일 때 실행
    }
    return 0;
}
    

조건 A: a > 5
조건 B: b < 10

위 코드에서 먼저 기준이 될 테스트를 하나 정하겠습니다.

입력값(a,b) 조건 A 조건 B 전체 결과
(6,7)

① 조건 A만 변경하여 확인

조건 A만 변경하려면, 입력값 a만 변경하고 b는 유지합니다.
입력값을 (3,7)로 바꿔 확인해 보겠습니다.

입력값(a,b) 조건 A 조건 B 전체 결과
(3,7) 거짓 거짓

조건 A를 바꾸니 조건문의 전체 결과가 참→거짓으로 바뀌었습니다.
조건 A는 독립적으로 전체 결과를 바꿀 수 있습니다.


② 조건 B만 변경하여 확인

이번에는 조건 B만 변경하기 위해 입력값 b만 바꾸고, a는 처음의 기준 케이스인 6을 유지합니다.
입력값을 (6,12)로 바꿔 확인합니다.

입력값(a,b) 조건 A 조건 B 전체 결과
(6,12) 거짓 거짓

조건 B를 바꾸니 조건문의 전체 결과가 참→거짓으로 바뀌었습니다.
조건 B 역시 독립적으로 전체 결과를 바꿀 수 있습니다.


📊 위의 내용을 요약한 표

테스트케이스 입력값 (a,b) 조건 A 조건 B 전체 결과
TC1(기준) (6,7)
TC2(조건 A만 바꿈) (3,7) 거짓 거짓
TC3(조건 B만 바꿈) (6,12) 거짓 거짓

위 표는 결과만 나타낸 것이며, 앞서 독립성 설명이 따로 진행되었습니다.

  • TC2에서는 조건 A만 바꾸어 결과가 바뀐 경우
  • TC3에서는 조건 B만 바꾸어 결과가 바뀐 경우입니다.

📊 누적 테스트케이스 조합별 MC/DC 퍼센트 정리표

누적 테스트케이스 독립 확인된 조건 MC/DC
TC1 + TC2 조건 A 독립 확인 50%
TC1 + TC3 조건 B 독립 확인 50%
TC1 + TC2 + TC3 조건 A, 조건 B 모두 독립 확인 100%

MC/DC 100%를 위해서는 최소한 TC1(기준 케이스) + TC2 + TC3 총 3가지 케이스가 필요합니다.


📝 다중 조건 커버리지(Multiple Condition Coverage)

🔷 개념 설명

다중 조건 커버리지(Multiple Condition Coverage)는 조건문에서 각각의 개별 조건들이 가질 수 있는 모든 참(true)/거짓(false)의 조합을 빠짐없이 검사하는 테스트 방법입니다.

예를 들어 조건이 2개 있다면 가능한 조합은 다음과 같이 4가지입니다.

  • (조건A:참, 조건B:참)
  • (조건A:참, 조건B:거짓)
  • (조건A:거짓, 조건B:참)
  • (조건A:거짓, 조건B:거짓)

이 모든 경우를 하나씩 실제로 테스트해서, 빠짐없이 검사하는 것이 다중 조건 커버리지입니다.


🔷 코드 예시를 통한 이해

다음의 조건문을 예시로 보겠습니다.


int main() {
    int x = 7, y = 15;
    if (x > 5 && y < 10) {
        // 참 분기
    } else {
        // 거짓 분기
    }
    return 0;
}
    

조건 A: x > 5
조건 B: y < 10

이때 가능한 모든 조합을 실제로 테스트하여 확인합니다.


🔷 독립적 테스트케이스 및 설명

① 조건 A=참, 조건 B=참 확인

입력값을 (7,5)로 설정합니다.

입력값(x,y) 조건 A 조건 B 전체 결과
(7,5)

② 조건 A=참, 조건 B=거짓 확인

입력값을 (7,15)로 설정합니다.

입력값(x,y) 조건 A 조건 B 전체 결과
(7,15) 거짓 거짓

③ 조건 A=거짓, 조건 B=참 확인

입력값을 (3,5)로 설정합니다.

입력값(x,y) 조건 A 조건 B 전체 결과
(3,5) 거짓 거짓

④ 조건 A=거짓, 조건 B=거짓 확인

입력값을 (3,15)로 설정합니다.

입력값(x,y) 조건 A 조건 B 전체 결과
(3,15) 거짓 거짓 거짓

📊 위 내용을 요약한 표

테스트케이스 입력값(x,y) 조건 A 조건 B 전체 결과
TC1 (7,5)
TC2 (7,15) 거짓 거짓
TC3 (3,5) 거짓 거짓
TC4 (3,15) 거짓 거짓 거짓

각 테스트케이스는 독립적이며 각각 전체 커버리지의 25%를 차지합니다.


📊 누적 테스트케이스 조합별 다중 조건 커버리지 퍼센트 정리표

누적 테스트케이스 확인된 조건조합 수 다중 조건 커버리지
TC1 1개 25%
TC1+TC2 2개 50%
TC1+TC2+TC3 3개 75%
TC1+TC2+TC3+TC4 4개 (모든조합) 100%

📝 경로 커버리지(Path Coverage)

🔷 개념 설명

경로 커버리지(Path Coverage)는 프로그램의 코드가 실행될 수 있는 모든 가능한 경로(실행 흐름)를 전부 검사하는 방법입니다.

하나의 코드에 여러 if문이나 중첩된 if문이 있다면, 이를 거치면서 만들 수 있는 모든 실행 경로를 테스트하는 것입니다.


🔷 코드 예시를 통한 이해

아래의 코드를 예시로 살펴보겠습니다.


int main() {
    int n = 7;
    if (n > 5) {
        if (n < 10) {
            // 경로 1
        } else {
            // 경로 2
        }
    } else {
        // 경로 3
    }
    return 0;
}
    

위 코드의 가능한 경로는 다음 세 가지입니다.

  • 경로 1: (n > 5)=참(n < 10)=참
  • 경로 2: (n > 5)=참(n < 10)=거짓
  • 경로 3: (n > 5)=거짓

이 각각의 경로를 실제로 확인해 봅니다.


🔷 독립적 테스트케이스 및 설명

① 경로 1 확인

입력값: n=7

입력값(n) (n>5) (n<10) 실행된 경로
7 경로 1

② 경로 2 확인

입력값: n=12

입력값(n) (n>5) (n<10) 실행된 경로
12 거짓 경로 2

③ 경로 3 확인

입력값: n=4

입력값(n) (n>5) 실행된 경로
4 거짓 경로 3

📊 위 내용을 요약한 표

테스트케이스 입력값(n) 실행된 경로
TC1 7 경로 1
TC2 12 경로 2
TC3 4 경로 3

각 테스트케이스는 독립적이며 각각 전체 커버리지의 33.3%를 차지합니다.


📊 누적 테스트케이스 조합별 경로 커버리지 퍼센트 정리표

누적 테스트케이스 확인된 경로 수 경로 커버리지
TC1 1개 33.3%
TC1+TC2 2개 66.6%
TC1+TC2+TC3 3개 (모든경로) 100%

📌 결론

  • 구문 커버리지
    모든 코드 라인을 최소 한 번 실행했는지 확인하는 기본 방법.
  • 결정 커버리지
    조건문의 참/거짓 결과에 따른 흐름이 모두 실행되는지 확인.
  • 조건 커버리지, 변경 조건/결정(MC/DC) 커버리지
    조건식 내부 개별 조건이 제대로 동작하는지 깊이 있게 검증 가능.
  • 다중 조건 커버리지, 경로 커버리지
    가능한 모든 조건 조합, 가능한 모든 코드 흐름을 철저히 검사하는 최고 수준의 테스트로 안전이 중요한 분야에서 필수적.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/10   »
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 30 31
글 보관함
반응형