구조 기반 테스트(Structure-based test)
프로그램 제어 흐름이나 자료 흐름 정보를 이용하여
테스트 케이스를 설계하는 방법이다.
프로그램의 내부 구조 정보를 기반으로
테스트 케이스를 설계하는 측면에서 구조적 테스트(Structural test),
화이트박스 테스트(White box test) 또는
글래스 박스 테스트(Glass-box test)라고도 한다.
제어 흐름 그래프
프로그램 구조를 매우 효과적으로 나타낼 수 있는 수단이다.
구조 기반 테스트를 수행할 때 우선 프로그램을
제어 흐름 그래프로 나타낸 후에 테스트 케이스를
추출하는 것이 일반적이다.
제어 흐름 그래프를 이루는 요소
기본 블록(Basic block)
단일 진입점과 단일 진출점을 가진 일련의
연속적인 실행 가능 한 문장들의 집합이다.
기본 블록의 문장들은 모두 함께 실행되거나
모두 함께 실행되지 않는다.
기본 블록은 각각 제어 흐름 그래프의
노드가 되며 박스로 표시한다.
제어 흐름(Control flom)
기본 블록 간의 실행 순서를 나타낸다.
만약 기본 블록 A에서 기본 블록 B로 간선이 있다면
A가 실행된 후에 B가 실행된다는 의미이다.
제어 흐름은 화살표로 표시한다.
ex)
코드를 제어 흐름 그래프로 나타내 본다면...
void findVinArray(int a[l, int n, int v) {
1: int i = 0;
2: int count = 0;
3: while (i < n) {
4: if (a[i] == v)
5: count+t;
6: i++;
}
7: printf ("number %d", count);
}
(예제 프로그램)
아래 표는 위 코드를 제어 흐름 그래프로 나타내기 위해 기본 블록들을 식별한 것이며
이를 제어 흐름 그래프로 나타낸 것이 제어 흐름 그래프의 예의 그림이다.
기본 블록
기본 블록 | 문장 | 진입점 | 진출점 |
B1 | {1, 2} | 1 | 2 |
B2 | {3} | 3 | 3 |
B3 | {4} | 4 | 4 |
B4 | {5} | 5 | 5 |
B5 | {16} | 6 | 6 |
B6 | {7} | 7 | 7 |
기본 블록 Bl은 1 2번 문장들로 구성되어 있다.
3번 문장이 B1에 속하지 않는 이유는 0번 문장의 실행이 종료되면
3번 문장으로 제어가 이동되어야 한다.
따라서
3번 문장은 1번 문장과 2번 문장과 동일한 블록에 포함될 수 없다.
또한,
3번 조건에 의해 조건이 참이 되면 4번 문장이 실행되나
거짓이 되면 4번 문장은 실행되지 않는다.
진출점이 두 개가 되기 때문에 3번과 4번 문장은
하나의 블록에 포함되지 않는다.
다른 기본 블록도 마찬가지로 구성된다.
아래 제어 흐름 그래프의 예 그림에는
기본 블록 간의 실행 순서를 고려하여
제어 흐름 그래프로 나타낸 것이다.
제어 흐름 그래프에서 볼 수 있듯이
프로그램 실행 흐름 및 프로그램 구조가
일목요연하게 나타나 있음을 알 수 있다.

제어 흐름 그래프상에서 입력 간선이 없는 노드를
시작 노드라 하고, 출력 간선이 없는 노드를 종료 노드라 한다.
위 그림에서 시작 노드는 B1, 종료 노드는 B6이다.
제어 흐름 그 래프상에서 프로그램 경로(Path)는
시작 노드에서 종료 노드까지 일련의 노드들이다.
예로 (B1, B2, B3, B4, B5, B2, B3, B5, B2, B6)는
프로그램상에 존재하는 수많은 프 로그램 경로 중의 하나이다.
구조 기반 테스트의 이해
가장 이상적인 구조 기반 테스트
프로그램의 모든 경로를 최소한 한 번은 실행하여 테스트하는 것이다.
그러나
불행하게도 모든 프로그램 경로를
한 번씩 실행시키는 작업은 현실 적으로 불가능하다.
그 이유는 프로그램 경로가 엄청나게 많이 존재할 수 있기 때문이다.
프로그램상에 존재하는 모든 가능한 경로를 테스트하는 대신
일부 경로만 테스트하는 방법을 사용한다.
대표적인 기본 경로 테스트인 문장 테스트, 분기 테스트,
조건 테스트, 다중 조건 테스트, MCDC 및 자료 흐름 테스트 등은
모든 프로그 램 경로를 테스트 하지 않고 일부 경로만 선정하는 기준을 제공한다.
문장 테스트
문장 테스트(Statement test)는 프로그램의
모든(실행 가능한) 문장을 최소한 한 번은 행하도록 요구한다.
문장 테스트는 그림 9.3의 절차로 수행된다.
(1) 테스트 대상 프로그램에 해당하는 제어 흐름 그래프를 작성한다..
(2) 모든 실행 가능한 기본 블록들을 지나가는 프로그램 경로 집합을 식별한다.
(3) 프로그램 경로 집합에 있는 각 프로그램 경로에 대해 다음을 수행한다.
A. 경로를 실행하는 입력 데이터를 식별한다.
B. 명세 등에서 해당 입력에 대한 기대 출력(Bxpected output)을 식별한다.
(문장 테스트를 수행하는 절차)
위 테스트 절차로 제어 흐름 그래프의 모든 블록이 수행되었다면
당연히 프로그램의 모 문장이 수행되었음을 의미한다.
프로그램에 대해 문장 테스트에 따라 테스트 케이스를 설계해보자.
명세 함수 foo는 입력 x와 z 또는 y와 z가 양수이거나 x, y 값에 상관없이 z가
10보다 크면 10을 반환한다. 그 외의 경우는 0을 반환한다.
int foo(int x, int y, int z) {
int W=0;
if (x>0 || y›0)
z=z+10;
if (z>10)
w=10;
return w;
}
(예제 프로그램)
우선 제어 흐름 그래프를 작성한다.
위 코드는 테스트 대상 프로그램의 제어 흐름 그래프이다.
이 제어 흐름 그래프에서 모든 블록을
실행할 수 있는 프로그램 경로 집합을 구한다.
이러한 프로그램 경로 집합은 유일하지 않으며
여러 개가 존재할 수 있다.
ex)
TS1={〈B1, B2, B3, B5〉, 〈B1, B3, B4, B5〉}이나
TS2={〈B1, B2, B3, B4, B5〉}는
모두 문장 테스트 요건을 만족하는 경로 집합들이다.
만약, TS1을 테스트할 경로 집합으로 선정 하였다면
전체 4개의 프로그램 경로 i.g., {〈B1, B2, B3, B4, B5〉, 〈B1, B2, B3, B5〉,
〈B1, B3, B4, B5〉, 〈B1, B3, B5〉} 중에서
2개의 경로를 선정하여 테스트하는 것이고
TS2 를 선정하였다면 4개의 경로 중에서
1개의 경로만 테스트하는 것이다.
예로,
TIS2를 테스트 경로 집합으로 선정한다.
다음 단계에서는 TS2에 있는 경로를 실행하는 입력 데이터 집합을 구한다.
입력 데이터 집합 (x=10, y=10. 2= 10)
프프로램 경로 <B1, B2, B3, B4, B5>를 실행하므로
모든 프로그램 문장을 실행한다.
또한,
명세에서 이 입력 데이터가 주어졌을 때
기대 출력은 10임을 알 수 있다.
테스트 케이스 | 입력 | 기대출력 | 실행된 블록 | ||
x | y | z | |||
1 | 10 | 10 | 10 | 10 | B1, B2, B3, B4, B5 |
(테스트 케이스)
따라서
만약 프로그램에 (x=10, y=10, 2=10)을 입력하여 실행했을 때
출력으로 10 이외 의 값이 반환된다면 프로그램에 결함이 있음을 알 수 있다.
그림 9.6의 식을 이용하여 테스트 케이스 집합에 의해
문장 테스트가 어느 정도 이루어졌는지 정량적으로 알 수 있으며
이를 문장 커버리지(Coverage)라고 한다.
제어 흐름 그래프에서 (X=10, y=10, 2=0)
〈B1, B2, B3, B5〉 경로를 실행하기 때문에
문장 커버리지는 전체 6개의 문장 중에서
5개의 문장이 실행되므로
5/6 X 100 = 83.3% 의 문장 커버리지를 갖는다.
만약, 실행이 안 된 블록 B4를 실행하는
테스트 케이스를 추가하면
100% 문장 커버리지를 가지게 될 것이다.
단축연산과 문장 커버리지
C, C++나 Java와 같은 프로그래밍 언어는
프로그램의 효율적인 실행을 위해
단축연산(Short-eiroult evaluation)을 수행한다.
단축 연산
&&(and), I(or)가 관여되는 논리 연산식에서
첫 번째 인자의 평가로 논리식의 결과가 결정될 때
다음에 나오는 인자를 평가하지 않는 방법이다.
만약, A&&B 연산식에서
A의 평가 결과가 1ale 면 B를 평가하지 않고
전체 연산의 결과는 false가 된다. 같은 이유로 A B에서
A가 true이면 B를 평가하지 않고
전체 연산의 결과는 true가 된다.
만약, 위 예제프로그램에서 "x>0 || y>0"가 단축 연산을 수행한다면
입력 데이터 집합 {(x=10), (y=0)} 에 모든 무장을 실행하지 못한다.
이유
논리식 "x>0 || y>0"에서 (x= 10, y= 10, z=10)에 대해
x>0 가 참이 되어 전체 논리식의 결과를 결정하기 때문에
y>0이 평가(실행)되지 않기 때문이다.
이와 같이 단축 연상을 수행하는 경우에는
x>0의 결과를 fale로 하여 y>0을 평가하게 할 필요가 있다.
따라서
(x=-10, y=10, z=10)을 입력하여 테스트하면
모든 문장을 실행할 수 있다.
결정 테스트
위 예제 프로그램의 문장 테스트(Statement test)는
더 적은 개수의 테스트 데이터들로 쉽게 만족할 수 있지만,
프로그램상에 존재하는 가능한 경우들을 모두 검증하지 못한다는 단점이 있다.
ex)
if (×<0) X= -x;
Z = X;
(예제 프로그램)
x가 0보다 작은 어떠한 값도 모든 문장을
한 번은 실행하게 하므로 문장 테스트의 요건을 만족한다.
이 경우, 변수 x가 0보다 크거나 같을 때는
프로그램이 올바르게 실행이 되는 지 알 수가 없다.
프로그램상에 나타난 모든 결정문(Decision)의 결과가
참이 되는 경우와 거짓이 되는 경우를
최소한 한 번은 실행하도록 요구한다.
예를 들어,
(x=3), (X=-5)}는 결정문을 true와 false가 되는 경우를 실행하므로
결정 테스트 요건을 만족하는 테스트 데이터 집합이다.
물론 항상 결정문의 결과로 true와 false 2개의 값만을 가지지는 않는다.
C 언어에서 스위치(Switch) 문장은 결정문이 여러 개의 결과를 가질 수 있다.
결정 테스트의 절차
(1) 테스트 대상 프로그램에 해당하는 제어 흐름 그래프를 작성한다.
(2) 아직 실행되지 않은 결정의 결과(들)에 도달하는 프로그램 경로 집합을 식별한다.
(3) 프로그램 경로 집합에 있는 각 프로그램 경로에 다음을 수행한다.
A. 경로를 실행하는 입력 데이터를 식별한다.
B. 명세 등에서 해당 입력에 대한 기대 출력(Expected output)을 식별한다.
(4) (2)-(3)을 모든 결정의 결과가 실행될 때까지 반복한다.
(결정 테스트를 수행하는 절차)
프로그램을 사용하여 결정 테스트에 따라
테스트 케이스를 설계 해보자.
이 프로그램에는 2개의 결정문이 있다.
x>0 || y>0과 z>10, 각각의 결정문이 true와false 값을 갖는 프로그램 경로 집합을 구한다.
이러한 프로그램 경로 집합은 유일하지 않으며 여러 개가 존재할 수 있다.
ex)
TS={〈B1, B2, B3, B5〉, 〈B1, B3, B4, B5〉}는
그러한 집합 중의 하나이다.
경로 〈B1, B2, B3, B5〉를 실행하는 입력 테스트 데이터는
결정문 "x>0 || y>0"이 true 값을 산출하고
z > 10이 false 결과를 갖도록 한다.
경로 〈B1, B2, B4, B5〉를 실행하는 입력 테스트 데이터는
"x>0 || y>0"이 false 값을 산출하고 z > 10이 true 결과를 갖는다.
아래 그림은 제어 흐름 그래프상에
이 경로 집합에 의해 실행된 경로들을
점선으로 표시한 것이다.

따라서
위 경로 집합을 실행하는 테스트 케이스 집합을 사용하여
프로그램을 실행하면 프로그램의 모든 결정문이 가질 수 있는
산출 결과들에 대해 프로그램을 테스트할 수 있다.
테스트 케이스 |
입력 |
기대 출력 | 결정 결과 | ||
x | y | Z | |||
1 | 10 | 10 | -10 | 0 | x>0 || y>0: tue |
z > 10: false | |||||
2 | -10 | -10 | 20 | 10 |
x>0 || y>0: false |
z>10: true |
(결정 테스트에 따라 구한 테스트 케이스 집합)
결정 커버리지를 구하는 식
아래 식을 이용하여 테스트 케이스 집합에 의해
결정 테스트가 어느 정도 이루어졌는지
정량적으로 알 수 있다.
위 표에서 테스트 케이스 1은 총 4개의 결정 결값 중에서
2개가 산출되기 때문에 4분의2 x100 = 50%의 결정 커버리지를 갖는다.
여기에 테스트 케이스 2가 추가되면 100% 결정 커버리지를 갖는다.
결정 테스트와 분기 테스트
ISO/IEC/IBEE 29119에서는 결정 테스트와
분기 테스트를 달리 정의한다.
ISO/IEC/IBBE 29119 에서 정의한 분기 테스트는
프로그램을 제어 흐름 그래프로 표현했을 때
제어 흐름 그래프상의 분기(또는 간선)들이
소한 한 번은 실행되기를 요구한다.
이러한 정의에 따르면 위 표 테스트 케이스 집합은
분기 테스트 요건을 만족한다.
그러나
결정문이 분기를 가지지 않은 경우에는
분기 테스트의 테스트 케이스 집합과 결정 테스트의
테스트 케이스 집합이 동일하지 않을 수 있다.
ex)
int foo(int x, int y) {
boolean k;
1: k=(x>10)&&(y>10);
2: return k+1;
}
(프로그램 예제)
1번 배정문은 결정식 "(x> 10)&&(y> 10)"의
결과를 변수 k에 대입한다.
따라서
결정 테스트로 테 스트 케이스 집합을 구하면
k가 true와 false가 되는 경우를 모두 나오게 하는
x와 J의 값을 사용하여 테스트해야 한다.
즉, x와 y가 모두 10보다 큰 경우와 x나 y가 10보다 작은 경우인
두 개의 테스 트 케이스가 필요하다.
반면에 분기 테스트는 제어 흐름 그래프를 작성했을 때
기본 블록 하나로만 구성되므로 이 블록을 실행하는 테스트 케이스이면
k가 어떤 값을 가지든지 상관없다.
따라서
하나의 테스트 케이스이면
분기 테스트의 요건을 만족한다.
테스트 요건 간의 관계
포용 관계(Subsumption relation)로서 서로 비교할 수 있다.
만 약 두 개의 테스트 요건이 C1과 C2가 있다고 가정할 때
Cl을 만족하는 테스트 케이스 집합이 C2를 만족한다면
C1은 C2를 포용한다고 말한다.
동일한 프로그램에 대해
결정 테스트를 만족하는 테스트 케이스 집합은
문장 테스트를 만족함을 쉽게 알 수 있다.
따라서
결정 테스트는 문장 테스트를 포용한다.
조건 테스트
DO-178B는 항공전자 시스템에서 사용되는
소프트웨어의 FAA(미국연방항공국)승인을 위한 지침으로
널리 사용되어오고 있다.
DO-178B 결정과 조건의 구분
A Condition is a Boolean expression containing no Boolean operators. A Decision is a Boolean expression composed of conditions and zero or more Boolean operators. A decision without a Boolean operator is a condition. If a condition appears more than once in a decision, each occurrence is a distinct condition.
조건은 논리 연산자 'and"나
"or"(CL Java 언어에서는 &&", "1")를
포함하지 않은 Boolean 식이고,
결정은 이러한 조건들이 논리 연산자를 사용하여
구성된 Boolean 식을 의미한다.
그러나
기본적으로 우리는 DO-178B의 정의를 따르지만
조건이나 결정이 반드시 true나 false를 가지는
Boolean 식으로 한정하지 않는다.
그 이유는 switch 문과 같은 경우에
true나 false가 아닌 여러 값을 가질 수 있기 때문이다.
ex)
if (x>0 && y<=- 3) {
x=y+4;
Y= y-1;
}
(예시 프로그램)
이 프로그램에서의 결정
2308&SK〈=-3'은 두 개의 조건 230과 3 〈=-3을
논리 연산자 "e"를 사용하여 구성하였다.
이때,
x 20과 3≤=-3은 식에 논리 연산자가 관여되지 않는
더 이상 분할될 수 없는 (기본) 조건이다.
이와 같이 관계 연산자(=, <>, <=)만을 적용하거나
Boolean 변수로만 이루어진 식을 조건이라 한다.
이 프로그램에 결정 테스트 요건을 만족하는
테스트 집합을 구해보면 다음과 같다.
{(x=4, 354), (3=-1, J=-4)} 아래 표에서 볼 수 있듯이
우리는 쉽게 위에서 주어진 테스트 집 합을 사용하여
프로그램을 실행하면 결정이 true가 되는 경우와
false가 되는 경우를 모두 실행할 수 있음을 알 수 있다.
결정 테스트를 만족하는 테스트 집합
테스트 데이터 | 조건 | 결정 | |
×>0 | Y<=-3 | x>0 &&y<=-3 | |
X=4, Y=-4 | true | true | true |
X=-1, Y=-4 | false | true | false |
그러나
이 테스트 집합은 조건 y<=-3이 false가 되는 경우
전혀 테스트 하지 못하는 사실도 알 수 있다.
조건 테스트(Condition test)는 프로그램의 조건에 나타난
모든 조건이 true가 되는 경우 와 false가 되는 경우
모두를 발생하게 하는 입력 데이터를
테스트 집합으로 사용할 것을 요구한다.
따라서
위 표의 테스트 집합은 결정 테스트 요건을 만족하지만,
y<=-3이 false가 되는 경우를 테스트하지 않기 때문에
조건 테스트를 만족하지 않는다.
만약 아래 표와 같이 (x=-1, y=-2)를
테스트 집합에 추가하면,
y<=-3이 fase가 되는 경우를 테스트 하므로
조건 테스트를 만족한다.
조건 테스트를 만족하는 테스트 집합
테스트 데이터 | 조건 | 결정 | |
×>0 | y <=-3 | x>0 && y<=-3 | |
X=4, y=-4 | true | true | true |
X=-1, Y=-4 | false | true | false |
X=-1, Y=-2 | false | false | false |
(1) 테스트 대상 프로그램에 해당하는 제어 흐름 그래프를 작성한다.
(2) 아직 실행되지 않은 조건의 결과(들)에 도달하는 프로그램 경로 집합을 식별한다.
(3) 프로그램 경로 집합에 있는 각 프로그램 경로에 다음을 수행한다.
A. 경로를 실행하는 입력 데이터를 식별한다.
B. 명세 등에서 해당 입력에 대한 기대 출력(Bx pected output)을 식별한다.
(4) (2)-(3)을 모든 조건의 결과가 실행될 때까지 반복한다.
(조건 테스트를 수행하는 절차)
프로그램을 사용하여 조건 테스트에 따라 테스트 케이스를 설계 해보자.
이 프로그램에는 2개의 결정이 있다.
첫 번째 결정×>OIl y>0에는 x>0 과 y> 0인
두 가지 조건으로 구성되어 있고,
두 번째 결정에는 2>10인 조건 하나로 이루어져 있다.
아래 표는 조건 테스트를 만족하는 테스트 케이스 집합을 보여 준다.
테스트 케이스 |
입력 |
기대 출력 | 조건 | ||||
x | y | z | x>0 | y >0 | Z>10 | ||
1 | 1 | -1 | 0 | 0 | true | false | false |
2 | -1 | 1 | 1 | 10 | false | true | true |
(테스트 케이스)
아래 식을 이용하여 테스트 케이스 집합에 의해
조건 테스트가 어느 정도 이루어졌는지
정량적으로 알 수 있으며, 이를 조건 커버리지 라고 한다.
더 알아보기
위 표에서 테스트 케이스 1은 총 6개의 결정 결과값 중에서
3개가 산출되기 때문에 6분의3 x 100=50%의 조건 커버리지를 갖는다.
여기에 테스트 케이스 "2"가 추가되면 100% 조건 커버리지를 갖는다.
조건 테스트는 결정 텍스트를 포용할까?
즉, 조건 테스트 요건을 만족하는 테스트 집합은
결정 테스트 요건을 만족할까?
대답은 No이다.
아래 표를 보면 쉽게 알 수 있다.
테스트 케이스 |
입력 | 기대 출력 |
조건 | 결정1 | 결정2 | |||
x | y | Z | x>0 | У > 0 | x> Olly > | z>10 | ||
1 | 1 | -1 | 0 | 0 | true | false | true | false |
2 | - 1 | 1 | 1 | 10 | false | true | true | true |
(조건 테스트를 만족하지만 결정 테스트를 만족하지 않은 테스트 케이스)
위 표를 보면 2개의 테스트 케이스로
모든 조건의 결과가 생성됨을 볼 수 있지만,
결정1은 true만 테스트 되고 false는 테스트 되지 않는다.
따라서
위 표의 테스트 케이스 집합은 조건 커버리지를 만족하지만,
결정 커버리지를 만족하지 않는다.
또한,
우리는 결정 커버 리지를 만족하지만
조건 커버리지가 반드시 만족되지 않는다는 사실을 이미 알고 있다.
따라서
조건 테스트와 결정 테스트는
서로 포용하지 않는다.
단축 연산과 조건 커버리지
단축 연산을 수행한다면
위 표의 테스트 케이스 집합은
100% 조건 커버리지를 만족하지 못한다.
아래 표는 단축 연산을 수행하는 경우
위 표의 테스트 케이스 집합에 따른 조건들의
평가 결과를 보여 준다.
아래 표에서 볼 수 있듯이 테스트 케이스 1은
x>0의 평가 결과로 전체 결정의 결과가 true가 됨을
알기 때문에 y>0 조건을 평가하지 않는다.
반면, 테스트 케이스 2는 x>0의 평가 결과가 false이기 때문에
결정의 결과를 알기 위해서는 y> 0을 반드시 평가해야 한다.
테스트 케이스 |
입력 | 기대 출력 |
조건 | 결정1 | 결정2 | |||
x | y | z | ×>0 | y>0 | ×>0||y>0 | Z>10 | ||
1 | 1 | -1 | 0 | 0 | true | - | true | false |
2 | -1 | 1 | 1 | 10 | false | true | true | true |
따라서 테스트 케이스 집합은 조건 y>0에서
false가 나오는 경우가 없으므로
100% 조건 커버리지 를 만족하지 못한다.
100% 조건 커버리지를 만족하기 위해서는
y>0을 false가 되는 테스트 데이터를 추가할 필요가 있다.
이때, 주의할 점은
x> 0을 false가 나오도록 하면서
y>0을 false가 나오도 록 해야 한다.
만약, x> 0이 true가 나오면
5>0이 실행되지 않기 때문이다.
다음 표는 100% 조건 커버리지를
만족하도록 추가된 테스트 케이스를 보여준다.
테스트 케이스 |
입력 | 기대 출력 |
조건 |
결정1 | 결정2 | |||
x | У | z | ×>0 | y>0 | ×>0||y>0 | Z>10 | ||
1 | 1 | -1 | 0 | 0 | true | - | true | false |
2 | -1 | 1 | 1 | 10 | false | true | true | true |
3 | -1 | -1 | 1 | 0 | false | false | false | false |
결정/조건 테스트
조건테스트에서 결정 테스트와 조건 테스트는
어느 쪽도 포용하지 않음을 기술하였다.
결정/조건 테스트(Decision Condition Test)는
결정 테스트와 조건 테스트를 모두 만족하는
테스트 케이스 집합을 설계하도록 요구한다.
(4) 테스트 대상 프로그램에 해당하는 제어 흐름 그래프를 작성한다.
(4) 아직 실행되지 않은 결정과 조건의 결과(들)에 도달하는 프로그램 경로 집합을 식별한다.
(3) 프로그램 경로 집합에 있는 각 프로그램 경로에 다음을 수행한다.
A. 경로를 실행하는 입력 데이터를 식별한다.
B.명세 등에서 해당 입력에 대한 기대 출력(Expected output)을 식별한다.
(4) (2)-(3)을 모든 결정과 조건의 결과가 실행될 때까지 반복한다
(결정/조건 테스트를 수행하는 절차)
더 알아보기
아래 표를 보면 2개의 테스트 케이스로
모든 조건의 결과가 생성됨을 볼 수 있지만,
결정1은 true만 테스트 되고 false는 테스트 되지 않는다.
따라서
위 표에서 "조건 테스트를 만족하지만 결정 테스트를
만족하지 않은 테스트 케이스"의 테스트 케이스 집합은
조건 커버리지를 만족하지만,
결정 커버리지를 만족하지 않는다.
따라서
결정/조건 테스트의 요건을 만족하기 위해서는
결정1을 false로 평가할 수 있는 테스트 케이스가
추가되어야 한다.
아래 표는 테스트 케이스 3을 추가하여
결정/조건 테스트를 만족하는 테스트 케이스 집합을 보여준다.
테스트 케이스 |
입력 | 기대 출력 |
조건 |
결정1 | 결정2 | |||
x | У | z | ×>0 | y>0 | ×>0||y>0 | Z>10 | ||
1 | 1 | -1 | 0 | 0 | true | false | true | false |
2 | -1 | 1 | 1 | 10 | false | true | true | true |
3 | -1 | -1 | 1 | 0 | false | false | true | false |
(결정 조건 테스트를 만족하는 테스트 케이스)
아래의 식을 이용하여 테스트 케이스 집합에 의해
결정 조건 데스트가 어느 정도에 어졌는지
정량적으로 알 수 있으며,
이를 결정/조건 커버리지라고 한다.
위 표의 테스트 케이스 1과 테스트 케이스 2로 이루어진
테스트 케이스 집합에 의해 달성되는
결정/조건 커버리지를 구해보자.
총 8개의 결정 및 조건 결과값 중에서
7개가 산출되므로 2 x 100=88%의
결정/조건 커버리지를 갖는다.
여기에 테스트 케이스 3을 추가하면
100% 결정/조건 커버리지를 갖는다.
위 표의 테스트 케이스 집합은
결정/조건 커버리지를 만족하는
최소 집합이 아니다.
아래 표와 같이 2개의 테스트 케이스를 사용하여
결정/조건 커버리지를 만 족할 수 있다.
테스트 케이스 |
입력 | 기대 출력 |
조건 |
결정1 | 결정2 | |||
x | У | z | ×>0 | y>0 | ×>0||y>0 | Z>10 | ||
1 | 1 | 1 | 0 | 0 | true | true | true | false |
2 | -1 | -1 | 1 | 20 | false | false | false | true |
(결정/조건 테스트를 만족하는 테스트 케이스)
다중 조건 테스트
더욱 강화된 테스트 케이스 집합을 얻기 위해서는
결정이 가질 수 있는 경우뿐만 아니라
결정을 구성하는 기본 조건들이 가질 수 있는
모든 가능한 조합까지도 프로그램 실행 중에
최소한 한번은 검증할 필요가 있다.
ex)
int multi (int x, int y) i
if (×>0 && y <=-3) {
X=y+4;
}
return x;
}
(예시 프로그램)
이 프로그램에서 결정 "x> 0&&y<=-3"은
두 가지 조건 x>0과 y 〈=-3을 논리 연산자
"&&"를 사용하여 구성하였다.
다중 조건 테스트(Multiple Condition Tiest)는
아래 표에서 보인 바와 같이
두 가지 조건의 모든 가능한 조합에 대해
테스트 집합을 구성한다.
(다중 조건 테스트)
id | x>0&8y<=3 | 테스트 데이터 | |
×>0 | y<=ー3 | ||
1 | true | true | (x: 10, y: -5) |
2 | true | false | (x: 10, y: -1) |
3 | false | true | (х: -5, у: -5) |
4 | false |
false | (x: -5, y: - 1) |
프로그램의 결정들에 사용된 모든 조건의 조합을
테스트할 수 있는 입력 데이터들을
테스트 데이터 집합으로 선정한다.
따라서
다중 조건 테스트는 문장 테스트,
결정 테스트, 조건 테스트 및 결정 조건 테스트를 포용한다.
(1) 테스트 대상 프로그램에 해당하는 제어 흐름 그래프를 작성한다.
(2) 아직 실행되지 않은 조건의 조합을 실행하는 프로그램 경로들을 식별한다.
(3) 프로그램 경로 집합에 있는 각 프로그램 경로에 다음을 수행한다.
A. 경로를 실행하는 입력 데이터를 식별한다.
B. 명세 등에서 해당 입력에 대한 기대 출력(Bxpected output)을 식별한다.
(4) (2)-(3)을 모든 결정에 포함된 조건들의 조합이 실행될 때까지 반복한다.
(다중 조건 테스트를 수행하는 절차)
더 알아보기
아래 표의 프로그램에는 2개의 결정이 있고,
첫 번째 결정을 2개의 조건으로 구성되어 있으며,
두 번째 결정은 하나의 조건으로 구성되어 있다.
테스트 케이스 1부터 테스트 케이스 4까지
첫 번째 결정을 구성하는 두 가지 조건의
모든 조합을 테스트한다.
또한,
테스트 케이스 1과 테스트 케이스 2는
두 번째 결정을 구성하는
모든 조건의 조합을 테스트함을 알 수 있다.
물론, 테스트 케이스 1과 2 대신에
테스트 케이스 2와 3 또는,
테스트 케이스 1과 4 또는,
테스트 케이스 3과 4도
두 번째 결정을 구성하는 조건의 조합을 테스트한다.
테스트 케이스 |
입력 | 기대 출력 |
x>0||y>0 | z>10 | |||
x | y | z | x>0 | У > 0 | x> Olly > | ||
1 | 1 | 1 | 1 | 10 | true | true | true |
2 | 1 | 0 | 0 | 0 | true | false | false |
3 | - 1 | 1 | 1 | 10 | false | true | true |
4 | - 1 | - 1 | 0 | 0 | false | false | false |
(다중 조건 테스트를 만족하는 테스트 케이스)
아래 식을 이용하여 테스트 케이스 집합에 따라
다중 조건 테스트가 어느 정도 이루어졌는지
정량적으로 알 수 있으며
이를 "다중 조건 커버리지"라고 한다.
위 표에서 테스트 케이스 1부터 테스트 케이스 3으로 구성된
3개의 테스트 케이스들에 의해 달성되는
다중 조건 커버리지를 구해보자.
총 6개의 조건 조합의 수에서
(첫 번째 결정에서 4개의 조건 조합과 두 번째 결정에서 2개의 조 건 조합)
첫 번째 결정의 조건 조합(false, false)이 누락되었기 때문에
5/6*100=84%의 다중조건 커버리지를 갖는다.
여기에 테스트 케이스 4를 추가하면
100% 다중조건 커버리지를 갖는다.
단축연산과 다중 조건 커버리지
단축 연산을 수행한다면 위 표의 테스트 케이스 1과 2는
x>0을 true로 평가하여 두 번째 결정 y>0 을 실행하지 않는다.
따라서
첫 번째 결정의 관점에서 테스트 케이스 1과 2는
중복되는 테스트 케이스라 볼 수 있다.
테스트 케이스 3과 4는 모두 필요하다.
이 테스트 케이스들은 첫 번째 조건 x>0을 false로 평가하여
결국, 두 번째 조건 y>0이 전체 결정의 값을 결정하기 때문이다.
두 번째 결정은
z>10인 한 가지 조건으로만 이루어져 있으므로
단축 연산의 영향을 받지 않으며,
이미 테스트 케이스 1과 4
또는,
테스트 케이스 3과 4에 의해
다중조건 커버리지를 만족한다.
테스트 케이스 |
입력 | 기대 출력 |
x>0||y>0 | z>10 | |||
x | y | z | x>0 | У>0 | x> Olly > | ||
1 | 1 | 1 | 1 | 10 | true | - | true |
3 | - 1 | 1 | 1 | 10 | false | true | true |
4 | - 1 | - 1 | 0 | 0 | false | false | false |
변형된 조건/결정 테스트(MCDC)
(Modified Condition Decision Adequacy Criterion, MCDC)
변형된 조건/결정 테스트는 말 그대로
조건 테스트와 결정 테스트 요건을
모두 만족해야 한다.
그러나
결정/조건 테스트와는 다른 점이 있다.
결정을 구성하는 각 조건이 독립적으로
결정의 결과에 영향을 미쳐야 한다.
여기에서 독립적이란 의미는
결정을 구성하는 다른 조건의 값은 고정하고
해당 조건의 값만 변경해야 한다는 의미다.
즉, MCDC는 각 조건이 결정을 구성하는
다른 조건들과는 무관하게
전체 결정의 평가에 영향을 미치는지를
알아보기 위한 테스트 케이스를 생성한다.
MCDC 테스트 요건을 만족하는 테스트 케이스를 구하기 위해서는
우선 결정을 구성하는 각 조건 C에 대해 C를 true로 만드는 테스트 케이스와
false로 만드는 테스트 케이스 두 개 를 선정한다.
이때,
C를 제외한 다른 모든 조건의 값은 동일하게 고정하고
전체 결정의 값이 true가 되는 경우와
false가 되는 경우가 발생하도록
테스트 케이스들을 선정한다.
ex)
결정 (A&&B)||C를 생각해보자.
MCDC를 만족하기 위해서는 다음과 같은
네 가지 경우가 발생할 수 있는 테스트 케이스들이 필요하다.
MCDC 테스트를 만족하는 테스트 케이스
테스트 케이스 | A | B | C | 결과 |
1 | true | true | false | true |
2 | false | true | false | false |
3 | true | false | false | false |
4 | true | false | true | true |
테스트 케이스 1과 테스트 케이스 2는 조건 A에 대한
MCDC를 만족하는 테스트 케이스들이고
테스트 케이스 1과 테스트 케이스 3은
조건 B에 대한 MCDC를 만족하는 테스트 케이스들이며
테스트 케이스 3과 테스트 케이스 4는
조건 C에 대한 MCDC를 만족하는 테스트 케이스들이다.
이와 같이 한 조건에 대해 MCDC 테스트 요건을 만족하기 위해서는
2개의 테스트 케이스를 필요로 하는데
이들을 독립 쌍(Independence Dair)이라고도 한다.
(1) 프로그램의 모든 결정에 대해 (2)-(6)과정을 반복한다.
(2) 주어진 결정을 트리 구조로 변환한다.
(3) 트리를 따라 위로 올라갈 때 다음 과정을 루트 노드에 도달할 때까지 반복한다.
A. 테스트 대상 조건 (간단히 테스트 조건)의 부모 노드가 OR이라면 형제 노드에게는 false를 할당한다.
B. 만약 부모 노드가 AND라면 형제 노드에게는 true를 할당한다.
(4) 트리 아래 방향으로 아직까지 값이 할당되지 못한 기본 조건들이 있다면 다음 과정을 통해 값을 전달 한다.
A. 방문한 노드가 AND 노드이고 true라면 자식 노드는 모두 true가 되어야 하며,
false라면 자식 노드 중 어느 하나는 false가 되어야 한다.
B. 방문한 노드가 OR 노드이고 true라면 자식 노드 중 어느 하나는 true이며,
false라면 자식 노드는 모두 false가 되어야 한다.
(5) 테스트 조건의 진릿값을 true와 false로 각각 설정한 후에 다음을 수행한다.
A. 트리의 각 조건에 할당된 진릿값을 생성할 수 있는 입력 데이터를 식별한다.
B. 명세 등에서 해당 입력에 대한 기대 출력(Bxpected output)을 식별한다.
(6) (2)-(5)과정의 결정을 구성하는 모든 조건에 대해 반복한다.
(MCDC 테스트를 수행하는 절차)
더 알아보기
다음 프로그램에 대해 MCDC 테스트 요건을
만족하는 테스트 케이스를 구해보자.
명세 함수 mcdc는 입력 x와 y가 모두 양수이거나
x, y 값에 상관없이 2가 10 보다 크면 20을 반환한다.
그 외의 경우는 10을 반환한다.
int mcdc(int x, int y, int z) {
int W=10;
if ((x>0 && y›O) || z>10)
W=W+10;
return w;
}
아래 그림은 "(x>0&&y> 0) | | z>10)"에 대해
테스트 조건이 x>0이라 할 때
나머지 조건들이 가져야 하는 값을 계산하는 과정을 보여 준다.
가장 왼쪽 그림은 결정 "(x> 10 && y>O) ||Z> 10)”을
"트리구조"로 표현한 것이며,
중간 그림은 테스트 조건의 부모가
AND이기 때문에 형제인 (y> 0)에
true를 할당한 것을 보여 준다.
가장 오른쪽 그림은 부모 노드가 OR 노드이기 때문에
조건 (z> 10)에 false를 할당 하였다.
루트 노드에 도달하였을 때
더는 값이 할당 안 된 조건이 없기 때문에
x>0의 독립 쌍을 구하였다.
(아래 표를 참고)
테스트 케이스 |
입력 | 기대 출력 |
(x>0&&y>0)||z>10 | ||||
x | y | z | x>0 | У>0 | x> Olly > | ||
1 | 1 | 1 | 1 | 20 | true | true | false |
2 | - 1 | 1 | 1 | 10 | false | true | false |
(×>0에 대해 MCDC 테스트를 만족하는 독립 쌍)
표에서 볼 수 있듯이
두 개의 테스트 케이스들은
테스트 조건 x>0의 진릿값은 변경하지만
나머지 조건들의 진릿값은 변경하지 않는다.
나머지 2개의 조건에도 통일한 과정을 반복하여
테스트 케이스들을 구하면 된다.
아래의 식을 이용하여 테스트 케이스 집합에 의해
MCDC 테스트가 어느 정도 이루어 졌는지
정량적으로 알 수 있으며
이를 MCDC 커버리지라고 한다.
더 알아보기
위 표에서 테스트 케이스 1과 테스트 케이스 2에 의해서
달성되는 MCDC 커버리지를 구해보자.
총 3개의 조건이 있으며(결정은 하나가 있고 이 결정에 3개의 조건이 있다)
표 9.13의 테스 트 케이스들은 조건 x> 0에 독립 쌍이다.
나머지 2개의 조건에 대한 독립 쌍은 포함되어 있지 않다.
따라서
MCDC 커버리지는 3분의1 x 100=34%의
MCDC 커버리지를 갖는다.
위 표에 100% MCDC를 만족하도록 테스트 케이스를 추가하자.
아래 표는 100% MCDC를 만족하는 테스트 케이스 집합이다.
테스트 케이스 1과 3은 조건 "y> 0"에 대한 독립 쌍이고
테스트 케이스 3과 4는 조건 "z>10"에 대한 독립 쌍이다.
테스트 케이스 |
입력 | 기대 출력 |
x>0||y>0 |
z>10 | |||
x | y | z | x>0 | У>0 | z>10 | ||
1 | 1 | 1 | 1 | 20 | true | true | false |
2 | - 1 | 1 | 1 | 10 | false | true | false |
3 | 1 | 0 | 0 | 10 | true | false | false |
4 | 1 | 0 | 20 | 20 | true | false | true |
(다중 조건 테스트를 만족하는 테스트 집합)
단축연산과 MCDC
MCDC는 Unigue-Cause MCDC라고 불린다.
Unique-Cause MCDC에서는 테스트 조건 이외의
나머지 조건들은 값이 고정되어 있어야 한다.
그러나
이러한 제약은 테스트 케이스의 식별이
순조롭지 않을 수 있다.
ex)
(X and Y or (X and 2) 이 결정에서는 조건 X가 반복되어 나타난다.
조건 테스트 절에서 언급하였듯 "DO-178C"에서는
이들을 개별적인 조건으로 간주한다.
If a condition appears more than once in a decision, each occurrence is a distinct condition.
이 경우, 첫 번째 X 조건을 변경하면
두 번째 X 조건도 따라서 변경될 수밖에 없기 때문에
Unique-Cause MCDC에서 요구하는 요건을
만족하는 테스트 케이스를 구할 수 없게 된다.
DO-178B의 개정판인 DO-178C에서는
이러한 문제를 해결하기 위해
Unique-cause MCDC 대신에
Masking MCDC를 사용할 수 있다.
Masking MCDC에서는
테스트 대상 조건이 유일하게
전체 결정의 값에 영향을 주는
유일한 조건임을 보여줄 수 있다면
테스트 조건 이외 나머지 조건들의 값이
반드시 고정되어 있을 필요가 없다.
예로, 다음 표는 첫 번째 조건 X의 독립 쌍이 될 수 있다.
X1 | Y | X2 | Z |
true | true | true | false |
false | true | false | true |
(위 표에서 X1은 첫 번째 X 조건을, X2는 두 번째 X 조건을 나타낸다.)
표에서 알 수 있듯이 두 번째 X의 조건은
첫 번째 X의 조건에 따라 값이 변하였으며
조건 Z의 값도 변경되었음을 볼 수 있다.
Y가 true가 되고 (X and Z)가 false가 되는 상황에서는
첫 번째 조건이 전체 결정의 값에 영향을 주는 유일한 조건이다.
즉, (X and Z)가 false가 된다면 Z 값은 변경되어도
전체 결정의 값에 영향을 미치지 않으므로
변경되어도 상관없다.
그러나
조건 Y의 값은 변경되어 서는 안 되고
true로 고정되어야 한다.
그 이유는 만약 Y가 독립 쌍 어느 한 곳에서 false가 된다면
첫 번째 조건 X의 값을 마스킹하게 되어
테스트 조건이 전체 결정의 값에 영향을
미치지 못하게 하기 때문이다.
단축 연산을 수행하는 경우에도
Unique-Cause MCDC는 문제가 될 수 있다.
"×>0에 대해 MCDC 테스트를 만족하는 독립 쌍"의 표는
단축 연산을 수행한다고 가정하면
테스트 조건 x>0이 true라면 y>0은 평가되지 않기 때문에
다음과 같이 변경되어야 한다.
테스트 케이스 |
입력 | 기대 출력 | (×>0&&y>0)||z>10 | ||||
x | y | z | ×≥0 | y>0 | z>10 | ||
1* | 1 | 1 | 1 | 20 | true | true | false |
2* |
-1 | 1 | 1 | 10 | false | 一 | false |
즉, 단축 연산이 있는 경우에
y>0이 true가 되는 테스트 케이스를 구할 수 없다.
그러나
Masking MCDC는 테스트 케이스 1*과
테스트 케이스 2*가 테스트 조건 x>0에 대한
독립 쌍이 될 수 있다.
기본 경로 테스트
지금까지 살펴본 테스트 방법은
프로그램 문장, 분기, 조건 등 프로그램 구조적 요소를
실행하는 테스트 데이터를 요구하였다.
즉, 직접적으로 특정 프로그램 경로를
실행하도록 요구하지는 않았다.
이와는 달리 프로그램 경로 중에서
기본 경로를 테스트하도록 요구한다.
기본 경로는 프로그램 나머지 경로를 만드는 데
기본이 되는 경로로 생각할 수 있다.
이러한 기본 경로의 개수는 순한 복잡도와 같다
기존 경로 집합을 사용하면 프로그램상에 존재하는
어떤 정로도 전형적으로 조합하여
표현할 수 있는 특징이 있다.
이를 좀 더 형식적으로 이해하기 위해서는
「간선 벡터(Bdge vector)」 개념을
이해 할 필요가 있다.
위 그림의 제어 흐름 그래프를 예시로
간선 벡터의 개념을 알아보자.
간선 벡터의 크기는
제어 흐름 그래프의 간선 개수로 결정된다.
예제 제어 흐름 그래프에서 간선 개수가 10 이므로
이 제어 흐름 그래프의 간선 벡터들은 크기가 10이다.
또한,
간선 벡터의 1-번째 항 목은 프로그램의 경로를 구성하는 데
1-번째 간선이 몇 번 사용되었는지를 나타낸다.
제어 흐름 그래프에서 눈여겨보아야 할 점은
간선에 번호를 할당했다는 점이다.
ex)
프로그램 경로<1→2→3→4→6→7→8→9→10>은
간선 벡터 <1, 1, 1, 1, 0, 1, 1, 1, 1, 1>로 표현한다.
마찬가지로 프로그램 경로 <1→2→3→4→6→7→8→9→10>은
3, 4, 6, 7, 8번 간선이 각각 두 번씩 경로에 나타나므로
간선 벡터 <1, 1, 2, 2. 0, 2, 2, 2, 1, 1>로 표현한다.
이와 같이 프로그램 경로를
벡터 형식으로 표현할 수 있기 때문에
한 프로그램 경로를 다른 프로그램 경로들의(선형적인)
조합으로 표현할 수 있다.
프로그램 경로 P1=<1→2→3→4→6→7→8→9→10>과
경로 P2=<1→2→9→10>으로 프로그램 경로
P3=<1→2→3→4→6→7→8→9→10>을 표현할 수 있다.
이를 이해하기 위해서 우선 P1, P2, P3를 다음과 같이 간선 벡터로 표현한다.
P1=<1, 1, 1, 1, 0, 1, 1, 1, 1, 1>, P2=<1, 1, 0, 0, 0, 0, 0, 0, 1, 1>,
P3=<1, 1, 2, 2, 0, 2, 2, 2, 1, 1>
이때,
P3=2P1+(-1)P2=2<1, 1, 1, 1, 0, 1, 1, 1, 1, 1>+(-1)<1, 1, 0, 0, 0, 0, 0, 0, 1, 1>가
성립됨을 알 수 있다.
이 경우에 P3는 P1과 P2의 선형적인 조합으로 표현되었다고 말하며
테스트 관점에서는 만약 P1과 P2가 테스트 되었다면
P3는 테스트 할 필요가 없다고 본다.
따라서
기본 경로 테스트의 목적은 프로그램의 모든 경로를
선형적 조합으로 표현할 수 있는
독립적인(Independent)경로 집합을 구하는 것이다.
여기에서 '독립적'이란 의미는 기본 경로 집합에 속한
각 기본 경로들은 기본 경로 집합에 속한
나머지 경로들의 선형 조합으로 표현되어서는 안 됨을 뜻한다.
기본 경로 집합을 구하는 여러 방법이 있으며
여기에서는 베이스라인(Baseline) 방법을 소개한다.
베이스라인 방법의 첫 번째 단계에서는
테스트 대상 프로그램에서 가장 베이스라인이 되는
프로그램 경로를 선정한다.
베이스라인 경로
어떤 특정 프로그램 경로를 지칭 하지 않지만,
예외처리를 나타내는 프로그램 경로보다는
대상 프로그램의 대표적인 기능 을 수행하는 프로그램 경로를
베이스라인으로 선택하는 것이 일반적이다.
아래 그림은 제어 흐름 그래프에서 베이스라인 경로로
1→2→3→4→6→7→8→9→10을 선정한 결과를 보여 준다.
다음에 생성할 경로들은 베이스라인 경로를 기반으로 생성된다.
두 번째 경로를 생성하기 위해서는 베이스라인의 첫 번째 결정값을 변경하고
베이스라인의 나머지 결정값들은 가능 한 한 변경하지 않는다.
아래 그림은 이와 같은 절차에 따라 생성된 두 번째 기본 경로 1→2→9→10이다.
베이스라인의 첫 번째 결정이 true이기 때문에 이를 false로 변경하였다
만약 경로를 생성하는 도중에
베이스라인 경로를 구성하는 결정들을 방문한다면,
임의대로 결정값을 선정하면 된다.
물론 이 경우에도 가급적,
프로그램의 대표적인 기능을 수행하도록 한다.
세 번째 경로를 선정하기 위해
베이스라인 경로의 두 번째 결정을 변화시킨다.
주의할 점으로 첫 번째 결정은
이제는 변경하지 않고 베이스라인 경로와 동일하게 한다는 점이다.
위 그림은 세 번째 기본 경로 1→2→3→5→7→8→9→10을 보여 준다.
이와 같은 과정을 베이스라인의 나머지 결정들에도 수행한다.
만약, 베이스라인 경로의 모든 결정에 수행하였다면
두 번째 구한 기본 경로에 베이스라인 경로와
동일한 과정을 적용 하여 기본 경로들을 생성한다.
만약 위의 과정이 프로그램의 모든 결정이 가질 만한
모든 가능한 값에 수행되었다면
기본 경로 집합이 모두 계산된 것이다.
위 제어 흐름 그래프에서 알 수 있 듯이
프로그램에 결정 2개가 모두 고려되었으므로
제어 흐름 그래프의 사이클로매틱 복잡도와 같은
3개의 기본 경로들을 모두 구했다.
'CertiFicate-prepare > CSTS-Study' 카테고리의 다른 글
[Study] CSTS 11장 - 테스트 프로세스 개요 (0) | 2024.08.07 |
---|---|
[Study] CSTS 10장 - 명세 기반 테스트 (0) | 2024.08.07 |
[Study] CSTS 8장 - 정적 테스트 (0) | 2024.08.07 |
[Study] CSTS 7장 - 테스트 자동화 (0) | 2024.08.02 |
[Study] CSTS 6장 - 소프트웨어 생명 주기 모델과 테스트 (0) | 2024.08.02 |