본문으로 건너뛰기
고급약 22분

모델 기반 테스트(Model-Based Testing) — 그림에서 테스트 케이스를 자동으로 뽑아내기

레슨 3에서 손으로 그린 상태 전이표를 모델로 삼아, 그 모델이 가능한 모든 경로를 스스로 순회하며 테스트 케이스를 생성하게 만드는 기법을 익힌다.

  • #모델기반테스트
  • #ModelBasedTesting
  • #상태전이
  • #테스트자동생성

개념

레슨 3의 표를 "실행 가능한 모델"로

레슨 3에서 우리는 주문 상태 전이표를 손으로 그렸다. 결제대기 → 결제완료 → 배송준비 → 배송중 → 배송완료라는 흐름에 취소와 반품이 얽히면서, 상태 8개에 정상 전이 11개가 나왔다. 그리고 그것을 상태-이벤트 매트릭스로 펼치자 8 × 5 = 40칸 중 32칸이 빈칸이라는 사실이 눈에 보였다.

여기까지 오면 자연스럽게 다음 질문이 생긴다.

표를 그리는 것까지는 사람이 했다. 그 표를 보고 경로를 골라내는 일까지 사람이 해야 하는가?

상태가 몇 개 안 될 때는 사람이 표를 보고 어떤 전이를 테스트할지 직접 고르면 된다. 그런데 상태가 10개, 20개로 늘어나고 이벤트도 다양해지면 "이 조합은 테스트했나?"를 손으로 추적하기 어려워진다. 레슨 3의 주문 예시조차 상태 8개에서 이미 40칸이었다. 상태가 15개, 이벤트가 10개면 150칸이다.

모델 기반 테스트(MBT) 는 이 상태 전이 다이어그램을 사람이 읽는 그림이 아니라, 도구가 읽고 순회할 수 있는 모델 로 만든다. 도구가 모델 위의 상태와 전이를 그래프처럼 훑으면서, 그 경로를 실행하는 테스트 케이스를 스스로 만들어낸다. 사람이 하던 "경로 고르기"를 알고리즘에게 넘기는 것이 이 기법의 전부다.

모델은 결국 그래프다 — 점과 화살표

MBT에서 말하는 "모델"이라는 말이 거창하게 들리지만, 실체는 레슨 3의 표와 완전히 같다. 표를 그래프 용어로 바꿔 부르는 것뿐이다.

레슨 3의 용어그래프 용어도구가 다루는 방식
상태(State)정점(Vertex, Node)그래프의 점
전이(Transition)간선(Edge, Arc)점과 점을 잇는 화살표
이벤트(Event)간선의 이름"이 화살표를 타려면 할 동작"
가드 조건(Guard)간선의 조건식조건이 참일 때만 그 화살표를 탈 수 있음
시작 상태시작 정점모든 경로가 여기서 출발

이 대응이 잡히면 그다음이 쉬워진다. "모든 전이를 한 번씩 실행한다"는 말은 그래프 용어로 "모든 간선을 한 번씩 지나는 경로를 찾는다" 가 되고, 이건 컴퓨터가 아주 잘하는 종류의 문제다. 사람이 표를 눈으로 훑으며 빠뜨림을 찾는 것보다, 알고리즘이 간선 목록을 하나씩 지워가는 쪽이 정확할 수밖에 없다.

한 가지 더 중요한 대응이 있다. 정점은 "확인할 것"이고, 간선은 "할 것"이다. 배송중이라는 정점에 도착했다면 거기서 확인해야 할 것은 order.status == 배송중, 송장 번호 존재, 취소 버튼 비노출 같은 것들이고, 송장 등록이라는 간선은 실제로 수행할 동작이다. 이 구분이 나중에 실행 스크립트를 만들 때 그대로 쓰인다.

자동 생성 파이프라인 — 다섯 단계

"자동으로 뽑아낸다"는 말이 마법처럼 들리지 않으려면 중간 단계를 봐야 한다. MBT 도구 안에서 벌어지는 일은 다섯 단계로 나뉜다.

[1] 모델 정의        상태 목록 + 전이 목록 + 시작 상태 + 가드
        ↓
[2] 커버리지 기준 선택   0-switch? 1-switch? 특정 상태 도달?
        ↓
[3] 순회 알고리즘 실행    기준을 만족할 때까지 그래프를 걸어다님
        ↓
[4] 경로 목록 산출      "결제대기 → 결제완료 → 배송준비 → 취소됨" 같은 줄들
        ↓
[5] 실행 가능한 형태로 변환  각 전이를 API 호출·UI 조작으로, 각 상태를 검증으로

사람이 하는 일은 [1]과 [2], 그리고 [5]에서 "각 전이가 실제로 어떤 동작인가"를 한 번 정의해주는 것이다. [3]과 [4]가 자동화되는 부분이다. 많은 사람이 MBT를 "테스트가 전부 저절로 만들어지는 도구"로 오해하는데, 실제로 자동화되는 건 이 두 칸이다. 그리고 이 두 칸이 바로 사람이 가장 많이 실수하는 칸이기도 하다.

손으로 따라가 보기 — 주문 모델 축소판

말로만 하면 감이 안 오니, 레슨 3의 주문 모델에서 반품 부분을 떼어낸 축소판으로 직접 해보자.

[1] 모델 정의. 상태 6개, 전이 7개다.

#현재 상태이벤트(간선 이름)다음 상태가드
T1결제대기결제 성공결제완료—
T2결제대기주문 취소취소됨—
T3결제완료상품 준비 시작배송준비—
T4결제완료주문 취소취소됨전액 환불 발생
T5배송준비송장 등록배송중—
T6배송준비주문 취소취소됨출고 처리 전까지만
T7배송중배송 완료 처리배송완료—

시작 상태는 결제대기, 더 나갈 곳이 없는 상태(취소됨, 배송완료)는 종료 지점이다.

[2] 커버리지 기준 선택 + [3] 순회. 0-switch를 고르면 도구는 "6개 정점을 전부 밟았는가"만 본다. 1-switch를 고르면 "7개 간선을 전부 지났는가"를 본다. 결과가 이렇게 갈린다.

[4] 경로 목록. 0-switch로 순회하면 경로 두 개면 끝난다.

P1: 결제대기 → 결제완료 → 배송준비 → 배송중 → 배송완료   (상태 5개 방문)
P2: 결제대기 → 취소됨                                   (남은 상태 1개 방문)

1-switch로 순회하면 네 개가 나온다.

P1: 결제대기 -T1-> 결제완료 -T3-> 배송준비 -T5-> 배송중 -T7-> 배송완료
P2: 결제대기 -T2-> 취소됨
P3: 결제대기 -T1-> 결제완료 -T4-> 취소됨
P4: 결제대기 -T1-> 결제완료 -T3-> 배송준비 -T6-> 취소됨

두 결과를 나란히 놓으면 0-switch의 한계가 그대로 드러난다. P1과 P2만 실행해도 상태 6개는 전부 밟는다. 그런데 T4(결제완료에서 취소)와 T6(배송준비에서 취소)은 한 번도 실행되지 않는다. 그리고 이 둘은 환불이 실제로 발생하는 전이 다. 상태만 다 밟았다고 안심하면 돈이 움직이는 경로 두 개가 통째로 빠진다는 뜻이다. 레슨 3에서 "1-switch가 실무의 기준선"이라고 한 이유가 여기서 숫자로 보인다.

[5] 실행 가능한 형태로 변환. 경로 P3을 실제 테스트로 옮기면 이렇게 된다. 간선은 동작, 정점은 검증이라는 규칙을 그대로 적용한다.

순서요소종류실제로 할 일
1결제대기정점 → 검증주문 생성 후 status == 결제대기 확인
2T1 결제 성공간선 → 동작결제 승인 API 호출
3결제완료정점 → 검증status == 결제완료, 결제 금액 일치 확인
4T4 주문 취소간선 → 동작취소 API 호출
5취소됨정점 → 검증status == 취소됨, 환불 1건 발생, 재고 복구 확인

여기서 눈여겨볼 것이 있다. 사람이 정의한 것은 정점 6개의 검증 방법과 간선 7개의 동작 방법, 합쳐서 13개 뿐이다. 도구는 이 13개를 조합해 경로를 만든다. 상태와 전이가 늘어나도 사람이 정의할 것은 정점 수 + 간선 수만큼만 늘어나는데, 조합될 수 있는 경로 수는 그보다 훨씬 빠르게 늘어난다. MBT의 경제성이 나오는 지점이 정확히 여기다.

순회 알고리즘은 실제로 무엇을 하는가

[3]단계를 조금 더 열어보자. 도구가 쓰는 순회 방식은 대개 두 종류로 나뉜다.

방식어떻게 도는가특징
결정적 순회아직 안 밟은 간선을 우선 골라 최단 경로로 찾아감케이스 수가 적고 매번 같은 결과가 나옴
무작위 순회(random walk)갈 수 있는 간선 중 하나를 무작위로 골라 계속 걸음예상 못 한 긴 조합이 나옴. 대신 매번 결과가 달라짐

두 방식 모두 정지 조건(stop condition) 이 필요하다. 그래프에 순환이 있으면(예: 반품접수 → 배송완료 → 반품접수) 무한히 걸어다닐 수 있기 때문이다. 그래서 실무에서는 "간선 커버리지 100%에 도달하면 멈춤", "정점 커버리지 80%에 도달하면 멈춤", "300스텝을 걸으면 멈춤" 같은 조건을 함께 지정한다.

무작위 순회는 특히 흥미롭다. 사람은 "결제 → 취소 → 재주문 → 취소 → 재주문"처럼 말이 안 되어 보이지만 실제로는 가능한 긴 조합 을 잘 떠올리지 못한다. 무작위 순회는 그런 조합을 자연스럽게 만들어낸다. 모듈 1 레슨 5에서 본 조합 폭발 문제를, 전부 도는 대신 오래 걸어다니는 것 으로 우회하는 접근이다.

손으로 뽑을 때와 무엇이 결정적으로 다른가

이제 핵심 차이를 정리할 수 있다. 표면적인 차이는 "빠르다"지만, 진짜 차이는 빠뜨림의 성격 에 있다.

레슨 3(수동 상태 전이 테스트)모델 기반 테스트
모델을 그리는 사람사람사람 (여기까지는 같다)
경로를 고르는 주체사람이 표를 보고 판단알고리즘이 순회하며 생성
커버리지 달성"대충 다 한 것 같다"기준을 기계적으로 100% 만족
빠뜨림이 생기는 곳표를 보는 눈, 집중력, 시간모델 자체 에만 생김
요구사항 변경 시케이스를 하나하나 다시 검토모델만 고치면 재생성
케이스 수 조절사람이 감으로커버리지 기준을 바꿔서

가장 중요한 줄은 "빠뜨림이 생기는 곳"이다. 수동으로 하면 빠뜨림이 여러 군데서 생긴다. 표를 잘못 읽어서, 시간이 없어서, 열 번째 행쯤에서 집중력이 떨어져서, 혹은 이번 스프린트에 급해서. 반면 MBT에서는 순회 단계에서는 빠뜨림이 생기지 않는다. 알고리즘은 지치지 않고, 급하다고 건너뛰지 않는다.

대신 빠뜨림이 한 곳으로 모인다 — 모델이다. 이건 좋은 소식이면서 동시에 무서운 소식이다.

모델이 틀리면 그 오류도 자동으로 100% 반영된다

MBT의 약점은 명확하다. 모델 자체가 실제 시스템과 다르면, 아무리 꼼꼼히 순회해도 잘못된 것을 꼼꼼하게 테스트하게 된다. 흔히 Garbage In, Garbage Out(GIGO)이라고 부르는 구조다.

구체적으로 보자. 위 모델에서 실수로 T6(배송준비에서 취소)의 가드 조건을 빼먹고 "언제든 취소 가능"으로 적었다고 하자. 그러면 어떻게 되는가?

  • 도구는 T6을 정상 전이로 취급해 경로를 만든다.
  • 그 경로가 통과하면 "출고 후 취소도 잘 되네"라는 결론이 나온다.
  • 실제로는 레슨 3의 사례 연구에 나온 그 결함 — 환불은 나가고 배송은 진행되는 상황 — 이 그대로 "정상"으로 기록된다.
  • 게다가 커버리지 리포트에는 간선 커버리지 100% 라고 찍힌다.

수동 테스트였다면 담당자가 실행하다가 "어? 출고됐는데 취소가 되네?"라고 의심할 여지가 있다. 자동 생성된 케이스는 모델이 기대한 대로 통과했으므로 아무도 멈춰 서지 않는다. 자동화가 오류를 더 빠르고 조용하게 확산시키는 것이다.

이건 모듈 18-A에서 배운 "AI가 생성한 테스트 케이스를 검증 없이 믿으면 안 된다"는 원칙과 근본적으로 같은 구조다. 생성기(모델이든 AI든)가 만든 산출물은 그 입력이 되는 사람의 이해가 정확해야 결과도 정확하다. 그래서 MBT를 쓸 때는 모델 자체를 리뷰 대상으로 올리는 절차 가 반드시 필요하다. 기획자·개발자와 함께 모델을 검토하는 시간이 곧 요구사항 리뷰가 된다 — 레슨 3에서 "표를 그리다 손이 멈추는 칸이 기획자에게 물을 항목"이라고 한 것과 같은 활동이다.

여기에 하나 더. 모델이 커버하는 것은 "정의된 전이"뿐이다. 레슨 3에서 가장 중요하다고 강조한 비정상 전이 — 매트릭스의 빈칸 32개 — 는 모델에 화살표가 없으므로 도구가 알아서 시도해주지 않는다. 빈칸을 테스트하려면 "이 전이는 거부되어야 한다"는 부정 전이(negative edge) 를 모델에 명시적으로 넣어줘야 한다. 이걸 모르면 "MBT를 도입했으니 상태 관련 테스트는 끝났다"는 가장 위험한 착각에 빠진다.

어디에 강한가 — 상태가 많거나, 자주 바뀌거나

MBT의 도입 가치는 두 축으로 판단한다. 상태 복잡도 와 변경 빈도 다.

첫째 축, 상태가 너무 많아 사람이 손으로 나열할 수 없는 시스템.

예시왜 사람이 감당 못 하나
워크플로 엔진(전자결재, 경비 승인)결재선이 조직마다 달라 상태·전이가 조합으로 늘어남
게임 캐릭터 스테이트 머신이동·공격·피격·경직·사망·부활이 서로 끼어들며 전이가 수십 개
통신 프로토콜 핸드셰이크재전송·타임아웃·중복 패킷까지 넣으면 상태가 폭증
구독·결제 라이프사이클체험 → 유료 → 연체 → 정지 → 해지 → 복구가 서로 얽힘

이런 시스템은 상태-이벤트 매트릭스가 A4 한 장을 넘어가고, 사람이 표를 보며 경로를 고르는 순간 이미 신뢰할 수 없어진다.

둘째 축, 모델이 자주 바뀌는 경우. 오히려 이쪽이 더 실질적인 이유다. 요구사항이 "배송 보류 상태를 하나 추가"처럼 바뀌었다고 하자.

수동MBT
해야 할 일기존 케이스 중 영향받는 것을 찾아내서 고치고, 새 케이스를 추가모델에 정점 1개와 간선 몇 개를 추가
위험"영향받는 케이스를 다 찾았는가"를 아무도 보장 못 함재생성하면 새 간선이 자동으로 커버에 포함됨
흔적케이스 문서와 실제 동작이 서서히 어긋남모델이 항상 유일한 정본

변경이 잦을수록 이득이 커진다는 점이 중요하다. 한 번 만들고 안 바뀌는 기능이라면 초기 모델링 비용을 회수하지 못한다. 반대로 분기마다 정책이 바뀌는 기능이라면, 모델 하나를 고치는 것으로 수십 개 케이스가 다시 정합해진다.

거꾸로 쓰지 말아야 할 신호 도 분명하다. 상태가 2~3개뿐이거나, 상태 전이보다 값 조합이 본질인 기능(그건 레슨 5의 페어와이즈 영역이다)이거나, 팀에 모델을 유지할 사람이 없는 경우다. 마지막이 실무에서 가장 흔한 실패 원인이다 — 도입한 사람이 퇴사하면 모델이 그날부터 낡기 시작한다.

도구 생태계 — 모델을 어떻게 입력하는가

개념만으로는 "그래서 뭘 어떻게 쓰라는 거지"가 남으니, 대표적인 오픈소스 도구가 모델을 어떤 형태로 받는지만 개념 수준에서 보자.

GraphWalker 가 가장 널리 쓰이는 오픈소스 MBT 도구다. 이름 그대로 "그래프를 걸어다니는(walk)" 도구다. 입력은 그래프 정의 파일 이다. yEd 같은 그래프 편집기로 그린 다이어그램을 GraphML로 저장하거나, JSON으로 직접 쓴다. 파일 안에는 두 종류만 들어간다.

정점(vertex)   v_결제대기, v_결제완료, v_배송준비, v_배송중, v_취소됨
간선(edge)     e_결제성공, e_주문취소, e_상품준비시작, e_송장등록, ...
               (각 간선에 출발 정점, 도착 정점, 가드 조건을 붙인다)

그리고 순회 방법을 이런 식으로 지정한다.

random(edge_coverage(100))     무작위로 걷되, 모든 간선을 밟으면 멈춤
quick_random(vertex_coverage(100))   빠르게 걷되, 모든 정점을 밟으면 멈춤

앞의 random/quick_random이 순회 알고리즘이고, 괄호 안이 정지 조건이다. 우리가 [2]·[3]단계에서 본 것이 그대로 한 줄의 설정으로 표현되는 셈이다.

실행할 때 GraphWalker는 경로를 걸어가면서 정점·간선의 이름과 같은 이름의 메서드를 테스트 코드에서 호출한다. e_송장등록 간선을 지나면 테스트 코드의 e_송장등록() 메서드가 실행되고, v_배송중 정점에 도착하면 v_배송중() 메서드가 실행되어 그 안의 검증이 돌아간다. 즉 QA가 짜는 것은 정점별 검증 코드와 간선별 동작 코드뿐 이고, 순서는 도구가 정한다. [5]단계의 "사람이 13개만 정의한다"는 말이 실제 코드에서는 이런 모습이다.

이 계열의 다른 도구로는 Java 단위 테스트에 모델을 붙이는 ModelJUnit, GraphWalker를 파이썬에서 쓰게 만든 AltWalker, 마이크로소프트가 만들었던 Spec Explorer(현재는 사실상 유지보수 중단) 등이 있다. 도구 이름을 외울 필요는 없다. 기억할 것은 어느 도구든 입력은 "정점 + 간선 + 커버리지 기준"이고, 출력은 "경로 목록"이라는 구조가 같다 는 점이다.

실무에서 왜 필요한가

사람이 스프레드시트로 추적하던 것을 도구에 넘긴다

상태가 많고 복잡한 시스템(결제, 예약, 승인 워크플로 등)에서는 "이 상태 조합, 테스트했었나?"를 사람이 스프레드시트로 추적하다가 빠뜨리는 일이 흔하다. 특히 릴리스 직전처럼 시간이 없을 때, 사람은 "중요해 보이는 것부터" 고르고 나머지를 조용히 건너뛴다. 모델 기반 테스트는 이 추적을 도구에 맡겨, 모델이 정확한 한 커버리지 기준을 빠짐없이 만족시킨다.

커버리지를 "숫자로 합의"할 수 있게 된다

수동 테스트에서 "상태 전이 테스트 충분히 했나요?"는 답하기 어려운 질문이다. MBT를 쓰면 이 질문이 "간선 커버리지 100%, 정점 커버리지 100%로 돌렸고, 비정상 전이 중 결제 관련 8개를 부정 전이로 모델에 넣었습니다" 같은 구체적인 문장이 된다. 릴리스 판단 회의에서 이 차이는 크다 — 모듈 6에서 배운 "테스트 결과를 근거로 말하기"의 상태 전이 버전이다.

모델링 자체가 요구사항 리뷰가 된다

MBT를 도입한 팀이 가장 먼저 얻는 이득은 사실 자동 생성이 아니라 모델을 만드는 과정 에서 나오는 경우가 많다. 상태와 전이를 도구가 읽을 수 있을 만큼 엄밀하게 적으려면 애매한 표현을 남겨둘 수 없기 때문이다. "출고 전까지만 취소 가능"을 모델에 넣으려면 "출고"의 정확한 시점을 정의해야 하고, 그 순간 기획자에게 질문이 간다. 코드가 한 줄도 없을 때 이 질문을 던질 수 있다는 점도 레슨 3과 같다.

다만 모든 곳에 쓸 도구는 아니다

상태가 단순하거나 자주 안 바뀌는 기능이라면, 레슨 3의 수동 방식이 도구를 도입하는 비용보다 오히려 더 실용적이다. QA는 "이 정도 규모의 상태 복잡도와 변경 빈도라면 모델링 비용을 들일 가치가 있는가"를 판단해야 한다. 도구를 아는 것보다 도구를 쓸지 말지 판단할 수 있는 것 이 실무에서 더 값어치 있는 능력이다.

실습 과제

과제 1 — 모델로 표현하기 (12분)

"쿠폰" 기능의 상태가 발급됨 → 사용가능 → 사용됨, 그리고 발급됨/사용가능 상태에서 만료됨으로도 갈 수 있다고 하자. 이걸 레슨 3 방식대로 상태 전이표로 그린 뒤, 0-switch(모든 상태 한 번씩 방문)와 1-switch(모든 전이 한 번씩 실행) 각각에 필요한 최소 경로 수를 세어본다.

해설 보기

상태: 발급됨, 사용가능, 사용됨, 만료됨 (4개) 전이: 발급됨→사용가능, 사용가능→사용됨, 발급됨→만료됨, 사용가능→만료됨 (4개, 시나리오에 따라 다를 수 있음)

시작 상태는 발급됨이다. 0-switch는 4개 상태를 밟기만 하면 되므로 경로 두 개면 된다.

P1: 발급됨 → 사용가능 → 사용됨      (상태 3개)
P2: 발급됨 → 만료됨                 (남은 상태 1개)

1-switch는 전이 4개를 전부 지나야 하므로 경로가 하나 더 필요하다.

P1: 발급됨 → 사용가능 → 사용됨
P2: 발급됨 → 만료됨
P3: 발급됨 → 사용가능 → 만료됨      (사용가능→만료됨 전이를 위해 추가)

여기서 놓치면 안 되는 것이 P3이다. 0-switch만 돌리면 "사용가능한 쿠폰이 유효기간이 지나 만료되는" 전이가 통째로 빠진다. 그런데 쿠폰 기능에서 실무 결함이 가장 자주 나는 곳이 정확히 거기다 — 만료된 쿠폰이 장바구니에 이미 적용되어 있던 경우. 전이 수가 상태 수보다 많거나 같다는 것 자체가, 1-switch가 왜 더 촘촘한 전략인지를 보여준다.

과제 2 — 파이프라인을 직접 돌려보기 (15분)

과제 1의 쿠폰 모델을 가지고 본문의 5단계 파이프라인 중 [5]단계를 해본다. 정점 4개와 간선 4개 각각에 대해 다음을 적는다.

  • 정점: 그 상태에 도착했을 때 무엇을 검증할 것인가 (화면·DB·API 응답 중 어디서 확인하는지까지)
  • 간선: 그 전이를 일으키려면 실제로 무슨 동작을 해야 하는가

그런 다음, 경로 P3(발급됨 → 사용가능 → 만료됨)을 위 정의를 조합해 하나의 테스트 절차로 펼쳐 쓴다.

해설 보기

정의는 예를 들면 이렇게 된다.

요소종류할 일
v_발급됨검증쿠폰 목록에 노출, status == ISSUED, 사용 버튼 비활성
v_사용가능검증status == AVAILABLE, 장바구니에서 선택 가능, 만료일 표시
v_사용됨검증status == USED, 할인 금액 반영, 재사용 시도 시 거부
v_만료됨검증status == EXPIRED, 목록에서 회색 처리, 적용 시도 시 거부
e_사용조건충족동작최소 주문금액 이상 담아 쿠폰을 사용가능 상태로 만듦
e_쿠폰사용동작주문에 쿠폰 적용 후 결제 완료
e_발급후만료동작유효기간을 지나게 함(시간 조작 또는 배치 실행)
e_사용가능중만료동작동일

P3을 펼치면 이런 절차가 된다.

  1. 쿠폰을 발급한다 → v_발급됨 검증
  2. e_사용조건충족 실행 → v_사용가능 검증
  3. e_사용가능중만료 실행 → v_만료됨 검증

여기서 나와야 할 질문이 이 과제의 진짜 목적이다. 3번에서 만료 시점에 그 쿠폰이 이미 장바구니에 적용되어 있었다면 어떻게 되는가? 장바구니 금액이 자동으로 되돌아가는가, 결제 단계에서야 실패하는가, 아니면 만료된 채로 할인이 유지되는가? 모델에는 이 화살표가 없다. 모델을 실행 형태로 옮기는 과정에서 모델의 빈틈이 드러난 것 이고, 이게 MBT를 도입한 팀이 초기에 가장 많은 결함을 찾는 이유다.

과제 3 — 잘못된 모델이 만드는 착각 (12분)

본문의 주문 축소판 모델에서, 누군가 실수로 T7(배송중 → 배송완료)을 빠뜨린 채 모델을 만들었다고 하자. 그 상태로 1-switch 순회를 돌리면 무슨 일이 벌어지는지 세 가지를 적는다.

  1. 생성되는 경로 목록은 어떻게 달라지는가
  2. 커버리지 리포트에는 뭐라고 찍히는가
  3. 이 실수를 발견할 수 있는 방법은 무엇인가
해설 보기

1. 경로 목록. T7이 없으면 배송중은 아무 데로도 나가지 못하는 막다른 상태가 된다. 경로는 이렇게 된다.

P1: 결제대기 → 결제완료 → 배송준비 → 배송중   (여기서 끝)
P2: 결제대기 → 취소됨
P3: 결제대기 → 결제완료 → 취소됨
P4: 결제대기 → 결제완료 → 배송준비 → 취소됨

배송완료 상태에는 아무 경로도 도달하지 않는다.

2. 커버리지 리포트. 여기가 무서운 지점이다. 모델에 정의된 간선이 6개(T1~T6)이고 그 6개를 다 밟았으므로 간선 커버리지 100% 로 찍힌다. 도구는 "있어야 하는데 없는 화살표"를 알 방법이 없다. 커버리지 100%는 "모델을 다 돌았다"는 뜻이지 "시스템을 다 테스트했다"는 뜻이 아니다.

3. 발견 방법. 세 가지가 현실적이다.

  • 정점 커버리지를 함께 본다. 간선 커버리지는 100%인데 정점 커버리지가 6개 중 5개(83%)에 그친다. 도달 불가능한 정점은 거의 항상 모델 오류의 신호다.
  • 모델을 사람이 리뷰한다. 기획자·개발자와 함께 그림을 보면 "배송중에서 끝나는 게 말이 되나요?"가 바로 나온다.
  • 실제 DB의 상태 값 목록과 대조한다. 레슨 3에서 배운 방법 그대로다 — 코드의 상태 enum에는 배송완료가 있는데 모델에 도달 경로가 없다면 불일치다.

첫 번째를 떠올렸다면 아주 좋다. 커버리지 지표를 한 개만 보면 안 된다는 것 이 이 과제의 핵심이다.

과제 4 — 모델링 비용 판단하기 (10분)

다음 두 기능 중 어느 쪽이 모델 기반 테스트 도구를 도입할 가치가 더 큰지 판단하고 이유를 적는다. 그리고 선택하지 않은 쪽은 대신 어떤 기법으로 다루면 좋을지도 함께 적는다.

  1. "다크모드 켜기/끄기" 토글 (상태 2개, 전이 2개)
  2. 여러 승인자를 거치는 "경비 승인" 워크플로 (상태 8개, 승인/반려/재상신 등 전이 다수, 요구사항이 분기마다 자주 바뀜)
해설 보기

2번이 명확하다. 상태와 전이가 적은 1번은 사람이 손으로 케이스 2~3개를 뽑는 게 도구 도입보다 훨씬 빠르다. 반면 2번은 상태 복잡도 와 변경 빈도 두 축을 모두 만족한다. 상태·전이 수가 많아 사람이 경로를 고르면 반드시 빠뜨리고, 요구사항이 자주 바뀌므로 모델 하나만 고치면 테스트가 재생성되는 장점이 실제 비용 절감으로 이어진다. 판단 기준은 "상태 복잡도 × 변경 빈도" 로 요약할 수 있다.

1번은 레슨 3의 수동 상태 전이 테스트로 충분하다. 오히려 다크모드에서 정말 중요한 것은 상태 전이가 아니라 "모든 화면에서 색상이 제대로 바뀌는가" 라서, 상태 전이보다 화면 목록 기반의 체크리스트나 시각적 회귀 테스트가 맞는 도구다. 기법을 고르기 전에 "이 기능의 위험이 어디에 있는가"를 먼저 묻는 것 이 순서다.

자가 체크리스트

  • 모델 기반 테스트가 레슨 3의 상태 전이표를 어떻게 확장하는지 설명할 수 있다
  • 상태·전이·이벤트·가드가 그래프의 정점·간선·간선 이름·조건에 어떻게 대응되는지 말할 수 있다
  • "정점은 검증, 간선은 동작"이라는 대응을 설명할 수 있다
  • 모델 정의 → 커버리지 기준 → 순회 → 경로 목록 → 실행 스크립트의 5단계를 순서대로 말할 수 있다
  • 5단계 중 실제로 자동화되는 단계가 어디인지 구분할 수 있다
  • 같은 모델에서 0-switch와 1-switch가 왜 다른 경로 수를 만드는지 예로 설명할 수 있다
  • 결정적 순회와 무작위 순회의 차이, 그리고 정지 조건이 왜 필요한지 안다
  • 수동 방식과 MBT에서 "빠뜨림이 생기는 위치"가 어떻게 달라지는지 설명할 수 있다
  • 모델이 틀리면 그 오류가 커버리지 100%로 포장되는 구조(GIGO)를 설명할 수 있다
  • 모델에 없는 비정상 전이는 도구가 시도해주지 않는다는 것을 안다
  • MBT가 유리한 조건 두 축(상태 복잡도, 변경 빈도)으로 도입 여부를 판단할 수 있다
  • GraphWalker 같은 도구가 모델을 어떤 형태(정점·간선 정의 + 커버리지 기준)로 입력받는지 개념 수준에서 설명할 수 있다

흔한 실수

  • 모델 기반 테스트를 모든 상태 기반 기능에 무조건 적용하려 한다. 상태가 단순한 기능은 레슨 3의 수동 방식이 오히려 더 빠르고 실용적이다. 판단 기준은 상태 복잡도와 변경 빈도 두 축이다.
  • "테스트가 자동으로 만들어진다"고 이해한다. 자동화되는 것은 순회와 경로 생성뿐이다. 모델을 만들고, 커버리지 기준을 고르고, 각 정점·간선을 실제 검증·동작으로 옮기는 것은 전부 사람의 일이다.
  • 모델을 한 번 만들고 실제 시스템이 바뀌어도 갱신하지 않는다. 모델이 낡으면 거기서 생성되는 테스트 케이스도 함께 낡아, 실제로는 안 맞는 걸 계속 "테스트했다"고 착각하게 된다.
  • 모델을 리뷰 없이 혼자 만든다. 빠뜨림이 전부 모델 한 곳으로 모이는 구조이므로, 모델 검토를 건너뛰면 MBT의 장점이 그대로 단점이 된다. 기획자·개발자와 함께 보는 시간이 필수다.
  • 생성된 테스트 케이스 개수만 보고 커버리지가 충분하다고 판단한다. 어떤 커버리지 전략(0-switch/1-switch)을 썼는지에 따라 실제로 확인되는 범위가 다르다.
  • 커버리지 지표를 하나만 본다. 간선 커버리지가 100%여도 도달하지 못한 정점이 있으면 모델이 잘못됐다는 신호다. 두 지표를 함께 본다.
  • 커버리지 100%를 "시스템을 다 테스트했다"로 읽는다. 그건 모델을 다 돌았다 는 뜻일 뿐이다. 모델에 없는 것은 애초에 세지도 않는다.
  • 비정상 전이가 자동으로 테스트될 거라 기대한다. 레슨 3에서 강조한 매트릭스의 빈칸은 모델에 화살표가 없으므로 도구가 시도하지 않는다. "거부되어야 한다"는 부정 전이를 명시적으로 모델에 넣어야 한다.
  • 무작위 순회 결과를 그대로 회귀 테스트로 쓴다. 매번 경로가 달라지므로 실패했을 때 재현이 어렵다. 회귀에는 결정적 순회 결과를 쓰고, 무작위 순회는 탐색용으로 따로 돌리는 편이 낫다.

참고 자료

  • ISTQB Glossary — Model-Based Testing — 모델 기반 테스트의 공식 정의
  • GraphWalker — 대표적인 오픈소스 MBT 도구. 그래프 정의 파일과 커버리지 기준을 어떻게 쓰는지 예제로 확인할 수 있다
  • Mark Utting & Bruno Legeard, Practical Model-Based Testing — 모델 기반 테스트 방법론의 대표 저서
  • ISO/IEC/IEEE 29119-4 — 테스트 기법 표준. 상태 전이 커버리지 항목이 MBT의 커버리지 기준과 그대로 대응된다
  • 이 모듈 레슨 3(상태전이·결정테이블) — 이 레슨의 전제. 거기서 손으로 그린 주문 상태 전이표가 여기서 모델의 입력이 된다
  • 이 모듈 레슨 5(페어와이즈) — 상태 전이가 아니라 값 조합이 본질인 기능은 이쪽이 맞는 도구다
  • 모듈 18-A(레슨 1) — "생성된 산출물을 검증 없이 믿으면 안 된다"는 이 레슨과 같은 원칙을 AI 생성 테스트 케이스에 적용한 사례

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

MBT — 모델 기반 테스팅
화면 흐름이나 상태 변화를 그림(모델)으로 먼저 그리고, 거기서 시험할 경로를 기계적으로 뽑아내는 방식.
API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
AI — 인공지능
사람이 규칙을 일일이 적지 않아도 데이터에서 규칙을 찾아 판단하는 소프트웨어.
JSON — 제이슨
프로그램끼리 값을 주고받을 때 쓰는 글자 형식. 괄호와 따옴표로 이름과 값을 짝지어 적는다.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
DB — 데이터베이스
데이터를 넣어 두고 꺼내 쓰는 저장소. 보통 표 여러 개가 서로 연결된 형태다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
ISO/IEC/IEEE — 세 단체 공동 표준
세 국제 단체가 함께 낸 표준이라는 표시.

용어집 전체 보기