SW Engineering & Testing (10) - Structural Testing

Charlie·2026년 5월 18일

SE&Testing

목록 보기
11/13

Path-based Testing

Introduction

Structural Programming

Introduction

구조적 프로그래밍 기법은 단순성을 지향한다. 복잡한 코드도 Sequence(순차), Selection(선택), Repetition(반복)이라는 3가지 기본 구조만으로 표현할 수 있다.

구조적 프로그램 생성 규칙은 아래와 같다.
1. 가장 단순한 형태에서 시작하라.
2. 어떤 작업 상태든 연속된 두 개의 작업 상태로 대체할 수 있다.
3. 어떤 작업 상태든 임의의 제어 구문으로 대체할 수 있다.
4. 규칙2와 규칙3은 원하는 만큼, 어떤 순서로든 자유롭게 적용할 수 있다.

프로그램 구조


규칙 적용의 예시는 아래와 같다.

| --|--|

CFG(Control Flow Graph)

구조적 테스트를 수행하려면 먼저 코드의 실행 흐름을 시각적으로 모델링해야 하며, 이를 위해 CFG를 작성한다.
CFG는 아래의 몇가지 구성요소를 가진다.

  • 노드(Nodes): 분기(Branch) 없이 순차적으로 실행되는 구문 또는 구문들의 연속인 '기본 블록(Basic Block)'을 의미한다.
  • 기본 블록(Basic Block): 첫번째 구문이 실행되면 블록 내의 모든 구문이 반드시 실행된다.
  • 간선(Edges): 제어 흐름을 나타낸다.






CFG를 작성할 때는 몇 가지 주의사항이 있다. 우선 if-return 구문을 작성할 때, if문 안과 밖에 모두 return이 있는 경우 두 return 노드는 반드시 별개의 노드로 구분되어야한다. 또한 while & for 구문에서 loop가 시작되기 전에 loop 변수를 초기화하는 dummy node가 암묵적으로 생성됨을 고려해 그려야한다.

Test Coverage

CFG를 작성한 이후에는 어떤 경로로 테스트를할 것인가를 "기준(Criteria)"으로 정립해야한다. 이 기준의 엄격함에 따라 다양한 커버리지로 분류된다.

Statement Coverage

프로그램 내의 모든 구문이 최소 한 번 이상 실행되도록 보장하는 가장 기본적인 커버리지이다. 반복문을 통해 입력값을 더하고 -1이 입력되면 종료 후 평균을 계산하는 프로그램이 있다고 가정하였을 때, 정상적으로 값이 입력되는 경로와 조건문들이 실행되는 경로를 포함하는 테스트 패스가 2개면 모든 노드(구문)을 방문하는 것이다.

Decision(Branch) Coverage

프로그램의 모든 진입점과 진출점이 최소 한 번 이상 호출되며, 프로그램 내의 모든 결정이 True & False의 결과를 최소 한 번 이상 가져야 한다. CFG 상의 모든 간선(Edge)을 순회하는 것과 같다.

Condition/Decision Coverage(C/DC)

결정 커버리지를 만족하는 동시에, 결정 구문 내부를 구성하는 각각의 개별 '조건(Condition)'들도 모두 참과 거짓을 최소 한 번씩 가지는 것을 말한다.

예시로 A||B의 경우 결정 자체도 true/false가 나와야 하고, A와 B도 각각 true/false가 나와야하므로 최소 테스트 셋은 (True, True)와 (False, False)가 된다.

Modified Condition/Decision Coverage(MC/DC)

가장 엄격한 기준으로, 각각의 개별 조건이 전체 결정의 결과에 '독립적으로 영향을 미친다(independently affect)' 는 것을 증명해야 한다. 이를 증명하려면 특정 조건의 값만 변경하고 나머지 조건들은 고정(holding fixed)했을 때, 전체 결과가 달라지는 것을 보여줘야 한다.

Excemple - (MC/DC)

  • AND 연산 (A && B)
    A의 독립성을 증명하기 위해선 B를 True로 고정하고 A를 T/F로 변경한다. 즉, (T,T)와 (F,T)가 필요하다. 반대로, B의 독립성을 증명할 때엔 A를 True로 고정하고 B를 T/F로 변경한다. 즉, (T,T)와 (T,F)가 필요하다. 결과적으로 최소 테스트 셋은 {(T,T), (T,F), (F,T)} 가 됩니다

  • OR 연산 (A || B)
    AND와 동일한 원리로 전체가 거짓이 되는 (F,F)를 기준으로 각 변수를 하나씩 True로 바꾼 {(T,F), (F,T), (F,F)} 가 최소 테스트 셋이 된다.

  • 윤년 계산기(isLeap(int year)) : 윤년 조건 ((year%4 == 0) && (year%100 != 0)) || (year%400 == 0)을 C1, C2, C3로 나눌 때, 각 조건(Variant)이 전체 결과에 영향을 미치는지 분석(예: B2는 B1의 C1 variant 등)하여 독립성을 증명하는 테스트 패스를 구성해야 한다.

Dataflow Testing

경로 기반 테스팅이 제어의 흐름에 집중했다면, 데이터 흐름 테스팅은 변수의 값이 어디서 정의되고 어디서 사용되는지 그 생명 주기를 추적하여 결함을 찾는다.

Dataflow Testing에는 두 가지 개념이 나오는데, 첫째로 Def는 Definition으로, 값이 메모리 위치에 저장되는 시점을 말한다. formal parameter로 사용되거나 input으로 값을 받을 때와 같은 상황에서 주로 발생한다. 두번째 개념은 Use로, 변수의 값이 접근(사용)되는 시점을 말한다. 대입 연산자의 우측에 올 때, 조건문의 평가에 사용될 때, 반환 및 출력되는 상황 등에서 발생한다.

Definition of Def-Use Pair

DU Pair는 특정 변수에 대해 (Def, Use)의 쌍을 묶은 것이다. 단, 이 쌍이 성립하려면 값이 정의된 후 사용되기 전까지 다른 값으로 재정의되지 않는 'Def-clear path' 여야만 한다.
def와 use가 같은 노드에 나타날 경우, 루프 내에서 정의가 사용 후에 발생하면 DU 쌍이 생성된다. def 사용 전에 발생할 경우, DU pair가 생성되지 않는다. 마지막으로 DU pair는 새로운 중간 정의 없이 정의가 제거된 경로여야 한다.

Program Slicing

프로그램 슬라이싱은 오류 추적과 프로그램 분석을 극대화하기 위해 코드를 잘라내어 분석하는 기법입니다
.
정적 후방 슬라이스 (Static, Backward Slice) S(V, n): 특정 시점(Slice criterion)에 해당하는 노드 n에서의 변수 집합 V의 값에 영향을 미치는 프로그램 내의 '모든 구문 조각(statement fragments)들의 집합'을 역추적하여 계산하는 방법입니다
.
적용 분야: 오류의 근원을 더 쉽게 찾는 디버깅(Debugging), 소프트웨어 유지보수, 코드 최적화, 프로그램 분석, 정보 흐름 제어 등에 유용하게 사용됩니다
.


  1. 테스트 기법 분석 (효과와 비용 비교)
    강의 자료에서는 이러한 구조적 테스팅(Code-based testing) 기법들과 명세 기반 테스팅(Spec-based testing, 예: 경곗값 분석, 동치 분할)을 분석 비용(Effort), 테스트 케이스의 수, 의미적 내용(Semantic content) 측면에서 비교합니다
    .
    의미적 내용 (Semantic Content): 명세 기반의 Boundary Value(경곗값)나 Path Testing은 의미론적 파악 요구도가 낮은 편입니다
    . 반면 구조적 기법인 Slice Testing이나 Data flow testing, 명세 기반의 Decision table 등은 구조 및 로직에 대한 높은 수준의 의미적 이해(High semantic content)를 요구합니다
    .
    테스트 케이스 식별 노력 (Test case identification effort): Data flow testing이나 Slice testing은 내부 로직을 일일이 추적해야 하므로 분석 노력이 매우 높게(High) 듭니다
    . 반면 동치 분할(Equivalence class)이나 경곗값 테스트는 비교적 쉽게 식별(Low effort)할 수 있습니다
    .
    테스트 케이스의 수 (Number of test cases): 경곗값 테스트(Boundary value testing)와 프로그램 슬라이싱(Slice testing)은 생성되는 테스트 케이스의 수가 굉장히 많아집니다
    . 반면 Decision table이나 Basic path testing은 상대적으로 적은 테스트 케이스를 생성합니다
    .
profile
찬찬히 써내려가는 개발일지

0개의 댓글