원인-결과 그래프로 복잡한 조건 조합을 검증하는 테스트 설계

원인-결과 그래프 테스트로 입력 조건과 출력 결과의 논리 관계, 제약, 결정 테이블을 체계적으로 검증하는 방법

2026-08-14 · 최초 발행 2025-12-17

조건 조합을 그래프로 드러내는 이유

원인-결과 그래프 테스트(Cause-Effect Graph Testing)는 요구 조건의 입력인 원인과 시스템 출력인 결과를 논리 관계로 연결해 테스트 케이스를 도출하는 블랙박스 테스트 설계 기법이다. 복잡한 비즈니스 규칙이나 검증 규칙을 시각적으로 정리하고, 결정 테이블(Decision Table)을 만들기 전 단계의 모델로 사용한다.

원인은 입력 조건 또는 사건을 나타내는 논리 변수다. 결과는 그 조건에 따라 시스템이 수행하는 동작 또는 출력의 논리 변수다. 둘 사이에는 AND, OR, NOT 같은 논리 연산을 배치하고, 필요한 경우 Requires, Exclusive, One-and-only-one, At-least-one 등의 제약을 명시한다.

이 모델은 조건 조합을 단순히 나열하는 대신, 규칙의 논리적 완전성과 일관성을 검토하게 한다. 그래프를 검증한 뒤 결정 테이블로 옮기면 테스트 케이스를 최소화하는 기반도 마련된다.

모순 발견정상부족충분입력 원인 정의출력 결과 정의논리 관계도출(AND/OR/NOT)제약 추가(Requires,Exclusive,One-and-only-one)그래프 검증(모순/도달 불가탐지)규칙 수정 제약 재정의결정 테이블 생성테스트 케이스 최소화(MCDC,Unique-cause 등)테스트 실행커버리지/추적성 검증테스트 보강 그래프 업데이트완료

그래프에 담는 논리와 제약

그래프는 원인·결과 노드와 AND, OR, NOT 논리 노드로 구성한다. 제약은 원인 간 의존성, 결과 간 상호배타성, 결과가 발생하는 조건의 유일성을 표현하는 데 쓴다. R은 Requires, E는 Exclusive, O는 One-and-only-one, I는 Inclusive-OR를 의미한다.

그래프만으로 끝나지 않는다. 제약 명세, 결정 테이블, 테스트 케이스 목록을 함께 산출하며, 요구사항 식별자와 각 노드 사이에 양방향 추적성을 부여할 수 있다. 이렇게 연결해 두면 요구사항 변경이 그래프와 테이블, 테스트에 미치는 영향을 따라가기 쉽다.

입력 조건을 수집한 뒤 논리식으로 정리하고 제약을 부여한다. 그래프에서 모순 규칙, 도달할 수 없는 결과, 제약 충돌이 발견되면 수정 단계로 되돌아간다. 이후 결정 테이블로 변환하고 케이스를 최소화한 다음, 실행 결과와 커버리지를 확인한다. 변경 이력을 관리하려면 그래프에도 버전 관리를 적용한다.

조합 폭발을 줄일 때는 그래프에 드러난 의존 구조를 이용해 불필요한 조합을 걷어낸다. MCDC, Unique-cause, Pairwise 같은 최소화 전략을 적용할 수 있다. 결과 커버리지, 제약 위반 검출률, 도달 불가 노드 수를 확인하고 결정 테이블 기준으로 완전성을 검증한다.

모델링 도구나 DSL, 테스트 케이스 생성기, SAT/SMT 기반 정형 검증기를 연계할 수 있다. 요구사항에서 그래프, 테이블, 테스트로 이어지는 링크를 관리하면 변경 영향도 분석과 회귀 테스트 자동화에도 도움이 된다.

다른 테스트 설계 기법과의 관계

기법 논리 조합 커버리지 복잡도 처리 일관성/모순 검출 안정성(재현성) 운영 편의(유지보수)
경계값 분석 낮음 낮음 낮음 높음 높음
원인-결과 그래프 높음 높음 높음 높음 중간
결정 테이블 테스트 높음 중간 중간 높음 높음

원인-결과 그래프는 복잡한 로직을 모델링하고 모순을 찾는 데 강점이 있다. 결정 테이블은 실행과 유지보수에 유리하며, 그래프의 후속 산출물로 함께 활용할 수 있다.

규칙 엔진과 안전 제어에 적용하는 방식

금융 인증 로직에서는 패스워드, OTP, 디바이스 신뢰처럼 다중 인증 조합과 리스크 정책을 원인과 결과로 분리한다. 예를 들어 "고위험 접속지", "OTP 등록", "디바이스 신뢰"를 원인으로 두고 "추가인증 요구", "거래 차단"을 결과로 둔다. Exclusive("거래 차단","추가인증 요구"), Requires("추가인증 요구" → "OTP 등록") 같은 제약을 표현한 뒤 결정 테이블로 바꾸고 MCDC 기반의 최소 케이스를 도출한다.

커머스 프로모션과 할인 규칙 엔진에서는 첫 구매 여부, 장바구니 금액, 쿠폰 유효성, 카테고리 제외 규칙이 주요 원인이 된다. 서로 다른 할인은 동시에 적용할 수 없다는 상호배타 제약을 모델에 반영하면, 유효한 조합만 선택해 테스트 케이스를 만들고 중복되거나 충돌하는 규칙을 정리할 수 있다.

임베디드 안전 제어에도 같은 접근이 적용된다. 차량 안전벨트 경고에서는 "속도 임계 초과", "벨트 착용 미완료", "좌석 점유"를 원인으로 두고 "시각 경고", "청각 경고"를 결과로 정의한다. 경고 강도 레벨에는 One-and-only-one 제약을, 청각 경고에는 Requires("청각 경고" → "속도 임계 초과") 제약을 둘 수 있다.

결정 테이블 행을 생성하는 코드

전제조건: Python 3.10+, 표준 라이브러리만 사용

# 파일: ceg_demo.py
# 목적: 원인-결과 그래프의 논리식과 제약으로부터 유효 조합(결정 테이블 행) 생성

from itertools import product

# 원인(C1..C3)
causes = ["first_time", "total_ge_100", "coupon_valid"]

def effects(assign):
    # 결과(E1..E2) 논리 정의
    E1_discount10 = assign["first_time"] and assign["coupon_valid"]
    E2_free_shipping = assign["total_ge_100"] or assign["coupon_valid"]
    return {"discount10": E1_discount10, "free_shipping": E2_free_shipping}

def constraints_ok(assign, eff):
    # 제약: Exclusive(E1, E2), Requires(E1 -> total_ge_100)
    if eff["discount10"] and eff["free_shipping"]:
        return False  # Exclusive 위반
    if eff["discount10"] and not assign["total_ge_100"]:
        return False  # Requires 위반
    return True

def decision_rows():
    rows = []
    for values in product([False, True], repeat=len(causes)):
        assign = dict(zip(causes, values))
        eff = effects(assign)
        if constraints_ok(assign, eff):
            rows.append((assign, eff))
    return rows

if __name__ == "__main__":
    rows = decision_rows()
    print(f"valid rows: {len(rows)}")
    for a, e in rows:
        print(a, "=>", e)

그래프 단계에서 합의한 논리식과 제약을 코드로 표현하고, 출력된 행을 결정 테이블에 반영한다. MCDC와 Pairwise 최소화는 추가 필터로 적용할 수 있다.

테스트 케이스와 규칙 검증에 남는 효과

중복되거나 불필요한 조합을 제거하면 테스트 케이스 수를 3060% 절감할 수 있다. 복잡도에 비례해 모순, 도달 불가, 상호배타 위반 같은 논리 결함의 사전 검출률은 2040%p 개선될 수 있다.

요구사항에서 테스트까지의 추적성이 강화되고, 변경에 따른 영향 범위를 파악하기 쉬워진다. 규칙 충돌과 예외 흐름도 가시화되어 리뷰 효율을 높일 수 있다.

소프트웨어 테스트원인-결과 그래프결정 테이블논리 커버리지요구사항 추적성