소프트웨어 개발

애플리케이션 테스트케이스 설계

출제기준 세세항목: 테스트 케이스 · 테스트 레벨 · 테스트 시나리오 · 테스트 지식 체계

핵심 요약

테스트는 무엇을 확인하는 일일까요

테스트는 프로그램을 실행해 보며 기대와 다르게 동작하는 곳을 찾는 활동입니다. '잘 돌아가는지 보여 주는 일'이라기보다 '숨은 결함을 드러내는 일'에 가깝습니다. 결함을 많이 찾아낸 테스트일수록 좋은 테스트라고 보는 것도 이 때문입니다.

테스트가 확인하는 방향은 두 가지입니다. 하나는 검증(Verification)으로, 만든 제품이 명세서에 적힌 대로 올바르게 만들어졌는지를 봅니다. 다른 하나는 확인(Validation)으로, 만든 제품이 실제 사용자가 원한 그것이 맞는지를 봅니다. 앞의 것은 '제품을 올바르게 만들었는가', 뒤의 것은 '올바른 제품을 만들었는가'라는 질문으로 기억하면 됩니다.

테스트는 실행 여부로도 나뉩니다. 프로그램을 실행하지 않고 문서나 코드를 사람이 읽으며 결함을 찾는 정적 테스트(리뷰, 워크스루, 인스펙션 등)와, 실제로 실행하며 결과를 보는 동적 테스트입니다. 이 항목에서 다루는 테스트 케이스 설계는 주로 동적 테스트를 위한 것입니다.

테스트를 할 때 기억할 원리

테스트에 관해서는 오랜 경험에서 나온 원리가 몇 가지 있습니다. 첫째, 테스트는 결함이 있다는 것은 보여 줄 수 있지만 결함이 없다는 것을 증명하지는 못합니다. 아무리 테스트를 통과했어도 아직 찾지 못한 결함이 남아 있을 수 있습니다. 둘째, 가능한 모든 입력과 경로를 다 시험해 보는 완벽한 테스트는 현실적으로 불가능합니다. 그래서 위험이 큰 곳에 우선순위를 두고 골라서 테스트합니다.

셋째, 테스트는 개발 초기부터 시작할수록 좋습니다. 요구사항 단계의 실수를 출시 직전에 발견하면 고치는 비용이 몇 배로 커집니다. 넷째, 결함은 고르게 퍼져 있지 않고 일부 모듈에 몰려 있는 경향이 있습니다. 이를 결함 집중이라고 하며, 적은 수의 모듈에서 결함 대부분이 나온다는 점에서 파레토 법칙과 연결해 설명하기도 합니다.

다섯째, 같은 테스트 케이스만 계속 반복하면 어느 순간부터는 새로운 결함을 찾지 못합니다. 같은 살충제를 계속 뿌리면 벌레가 내성이 생기는 것에 빗대어 살충제 패러독스라고 부릅니다. 그래서 테스트 케이스를 주기적으로 점검하고 새로 보강해야 합니다. 여섯째, 테스트 방법은 소프트웨어의 성격에 따라 달라야 합니다(정황 의존). 은행 시스템과 게임은 중요하게 볼 곳이 다릅니다.

마지막으로 오류-부재의 궤변이 있습니다. 결함을 다 찾아 고쳤더라도, 그 소프트웨어가 사용자의 요구를 채우지 못한다면 품질이 좋다고 할 수 없다는 뜻입니다. 앞에서 말한 확인(Validation)이 왜 필요한지를 보여 주는 원리입니다.

테스트 케이스와 테스트 오라클

테스트 케이스는 테스트 한 건을 실행하기 위해 필요한 정보를 묶어 적은 것입니다. 보통 식별 번호, 무엇을 시험하는지(테스트 항목), 실행 전에 갖춰져 있어야 할 조건(사전 조건과 환경), 넣을 값(입력 데이터), 실행 절차, 그리고 나와야 할 결과(예상 결과)가 들어갑니다. 핵심은 예상 결과를 실행 전에 미리 적어 둔다는 점입니다. 실행해 보고 나서 '이 정도면 맞겠지' 하고 판단하면 테스트가 아니라 감상이 됩니다.

좋은 테스트 케이스는 요구사항에서 출발합니다. 요구사항 하나하나에 대해 어떤 케이스로 확인하는지 연결해 두면, 빠진 요구사항이 없는지 추적할 수 있습니다. 또 정상적인 입력뿐 아니라 잘못된 입력, 경계에 걸친 입력도 함께 다뤄야 결함을 놓치지 않습니다.

실행 결과가 맞는지 판정하려면 '정답'을 알려 주는 기준이 필요합니다. 이것을 테스트 오라클이라고 합니다. 모든 입력에 대해 기대 결과를 제공하는 참(True) 오라클, 미리 골라 둔 일부 입력에 대해서만 기대 결과를 제공하는 샘플링 오라클, 샘플링 오라클에 경험적 추정을 더해 나머지 입력의 결과를 어림하는 추정(휴리스틱) 오라클, 그리고 이전 실행 결과나 다른 버전의 결과와 비교해 일관되는지를 보는 일관성 검사 오라클이 있습니다. 참 오라클은 판정이 가장 확실하지만 만들기 어렵고 비용이 크기 때문에, 실무에서는 나머지 방식을 섞어 씁니다.

블랙박스 기법: 명세만 보고 케이스 만들기

블랙박스 테스트는 프로그램 내부를 보지 않고, 명세에 적힌 입력과 출력만 보고 케이스를 만듭니다. 상자 안은 캄캄해서 보이지 않는다고 생각하는 것입니다. 기능이 요구대로 동작하는지 확인하는 데 쓰이므로 기능 테스트라고도 합니다.

동등 분할(동치 분할)은 입력값을 '프로그램이 같은 방식으로 처리할 것 같은 묶음'으로 나누고, 묶음마다 대표값 하나씩만 고르는 기법입니다. 예를 들어 나이가 0~19면 청소년 요금, 20~64면 일반 요금, 65 이상이면 경로 요금이라면 세 묶음에서 10, 40, 70처럼 하나씩 고르면 됩니다. 같은 묶음 안의 값은 같은 결과를 낼 것이라고 보고 테스트 수를 줄입니다.

경계값 분석은 결함이 묶음의 가장자리에서 자주 생긴다는 경험에서 나왔습니다. '20 이상'을 '20 초과'로 잘못 쓰는 실수가 흔하기 때문입니다. 그래서 경계 바로 위아래 값을 고릅니다. 고르는 방식에는 두 가지가 있습니다. 유효 범위의 경계값과 그 바로 바깥의 무효값을 함께 고르는 방식(1~100이 유효 범위라면 0, 1, 100, 101)과, 유효 범위 안에서 최솟값, 최솟값+1, 중간값, 최댓값-1, 최댓값을 고르는 방식입니다. 문제에서 어느 기준을 말하는지 보고 판단하면 됩니다.

원인-결과 그래프는 입력 조건(원인)들과 그 조합이 만들어 내는 출력(결과)의 관계를 그래프로 그린 뒤, 이를 결정 테이블로 바꿔 케이스를 뽑는 기법입니다. '회원이면서 쿠폰이 있으면 할인'처럼 여러 조건이 얽힌 기능에서, 의미 있는 조합만 골라 효율적으로 테스트할 수 있습니다. 조건과 결과를 처음부터 표로 정리하는 결정 테이블 테스트도 같은 생각에서 나왔습니다.

이 밖에 테스트하는 사람의 경험과 감으로 결함이 잘 날 만한 곳을 짚어 보는 오류 예측, 같은 명세로 만든 여러 버전의 프로그램에 같은 입력을 넣어 결과를 비교하는 비교 검사가 있습니다. 오류 예측은 다른 기법으로 놓친 부분을 보충하는 용도로 많이 씁니다.

화이트박스 기법: 내부 구조를 보고 케이스 만들기

커버리지 엄격도 (오른쪽일수록 더 엄격)
구문<결정(분기)<조건/결정<변경 조건/결정(MC/DC)<다중 조건

화이트박스 테스트는 소스코드의 논리 구조를 들여다보며 케이스를 만듭니다. 상자가 투명해서 안이 다 보인다고 생각하는 것입니다. 모든 문장이 한 번은 실행되는지, 모든 갈림길이 양쪽 다 지나가는지 같은 것을 확인하므로 구조 테스트라고도 합니다.

기초 경로 검사는 코드의 흐름을 그래프로 그리고, 서로 독립적인 실행 경로들을 찾아 각각을 한 번씩 지나가도록 케이스를 만드는 기법입니다. 독립 경로가 몇 개인지는 맥케이브(McCabe)의 순환 복잡도로 계산하는데, 흐름 그래프에서 '간선 수 - 노드 수 + 2'로 구합니다. 참·거짓 두 갈래로 나뉘는 판단 문의 개수에 1을 더해도 같은 값이 나옵니다(여러 갈래로 나뉘는 분기가 있으면 달라집니다). 복잡도가 클수록 코드가 복잡하고 테스트할 경로도 많다는 뜻입니다.

제어 구조 검사는 코드의 특정 구조에 초점을 맞춥니다. 조건문 안의 논리식을 집중해서 보는 조건 검사, 반복문이 0번, 1번, 여러 번 돌 때를 확인하는 루프 검사, 변수가 정의되고 사용되는 위치를 따라가는 데이터 흐름 검사가 여기에 속합니다.

얼마나 꼼꼼히 테스트했는지는 커버리지로 잽니다. 구문(문장) 커버리지는 모든 문장이 최소 한 번 실행되면 됩니다. 결정(분기) 커버리지는 각 if 문 전체의 결과가 참과 거짓으로 모두 나와야 합니다. 조건 커버리지는 if 문 안의 개별 조건 하나하나가 참과 거짓을 모두 가져야 합니다. 조건/결정 커버리지는 앞의 둘을 동시에 만족해야 하고, 변경 조건/결정 커버리지(MC/DC)는 여기에 더해 각 개별 조건이 전체 결과를 혼자서 바꿀 수 있음을 보여야 합니다. 다중 조건 커버리지는 개별 조건들의 가능한 모든 조합을 다 시험하므로 가장 엄격합니다.

주의할 점은 조건 커버리지와 결정 커버리지가 서로를 보장하지 않는다는 것입니다. 'A 그리고 B'에서 (A 참, B 거짓)과 (A 거짓, B 참)을 넣으면 각 조건은 참·거짓을 다 가졌지만, 전체 결과는 두 번 다 거짓이라 결정 커버리지는 채우지 못합니다.

테스트 레벨: 작은 조각부터 전체까지

테스트는 한 번에 끝나지 않고, 대상의 크기에 따라 단계를 밟아 올라갑니다. 이 단계를 테스트 레벨이라고 합니다. 단위 테스트는 함수나 클래스, 모듈처럼 가장 작은 조각을 따로 떼어 시험합니다. 주로 개발자가 직접 하며, 화이트박스 기법이 많이 쓰입니다. 통합 테스트는 단위 테스트를 통과한 조각들을 합쳐서, 조각 사이의 인터페이스와 상호작용에 문제가 없는지 봅니다.

시스템 테스트는 완성된 시스템 전체를 실제 운영과 비슷한 환경에서 시험합니다. 기능이 명세대로 동작하는지 보는 기능 테스트와 함께, 성능·보안·회복 능력처럼 기능 외의 품질을 보는 비기능 테스트도 이 단계에서 많이 합니다. 인수 테스트는 최종 사용자나 고객이 '이 제품을 받아들일지'를 판단하는 마지막 단계입니다.

인수 테스트에는 알파 테스트와 베타 테스트가 있습니다. 알파 테스트는 개발자가 있는 장소에서 사용자가 시험하고, 개발자가 옆에서 지켜보며 문제를 기록합니다. 통제된 환경입니다. 베타 테스트는 여러 사용자가 자기 실제 환경에서 개발자 없이 써 보고 문제를 보고하는 방식으로, 통제되지 않은 환경입니다. 그 밖에 실제 사용자가 업무에 쓰기 적합한지 확인하는 사용자 인수, 계약 조건을 만족하는지 보는 계약 인수, 법규를 지키는지 보는 규정 인수, 운영·관리 측면에서 받아들일 수 있는지 보는 운영상 인수 같은 구분도 있습니다.

이 레벨들은 V 모델과 짝을 이룹니다. V 모델은 왼쪽 날개를 따라 요구사항 분석, 시스템 분석(명세), 설계, 상세 설계와 구현으로 내려가고, 오른쪽 날개를 따라 테스트로 올라오는 모양입니다. 같은 높이에 있는 단계끼리 대응하는데, 구현(상세 설계)은 단위 테스트로, 설계는 통합 테스트로, 분석(시스템 명세)은 시스템 테스트로, 요구사항은 인수 테스트로 확인합니다. 왼쪽에서 무엇을 정했는지가 오른쪽에서 무엇으로 검사할지를 정하는 셈입니다.

테스트 시나리오: 케이스를 흐름으로 엮기

테스트 케이스 하나는 '이 조건에서 이 값을 넣으면 이 결과'라는 한 점을 확인합니다. 하지만 실제 사용자는 한 점이 아니라 흐름을 따라 움직입니다. 로그인하고, 상품을 담고, 쿠폰을 쓰고, 결제하는 식입니다. 테스트 시나리오는 이렇게 여러 테스트 케이스를 실제 사용 순서대로 묶어, 어떤 순서로 실행할지 정한 실행 계획입니다.

시나리오가 있으면 앞 단계의 결과가 다음 단계의 조건이 되는 상황, 예를 들어 장바구니에 담은 상품이 결제 화면에 그대로 넘어오는지 같은 것을 확인할 수 있습니다. 케이스를 낱개로만 실행하면 놓치기 쉬운 부분입니다.

시나리오를 쓸 때는 몇 가지를 지키면 좋습니다. 시스템별, 모듈별, 기능 항목별로 나눠 따로 작성해 범위를 분명히 합니다. 고객의 요구사항과 설계 문서를 바탕으로 쓰되, 각 단계에 식별자와 순서, 사전 조건, 예상 결과를 적습니다. 정상 흐름뿐 아니라 중간에 결제가 실패하거나 사용자가 뒤로 가기를 누르는 것 같은 예외 흐름도 따로 시나리오로 만들어 둡니다.

테스트 지식 체계: 테스트를 바라보는 여러 기준

테스트 지식 체계는 테스트를 계획하고 수행하는 데 필요한 지식을 기준별로 정리한 틀이라고 이해하면 됩니다. 앞에서 본 원리, 기법, 레벨에 더해, 테스트를 어떤 순서로 진행하는지(프로세스), 어떤 목적으로 하는지(유형), 무엇을 남기는지(산출물)를 함께 알아 두면 전체 그림이 잡힙니다.

테스트 프로세스의 단계 이름은 자료마다 조금씩 다릅니다. 국제 자격(ISTQB) 계열에서는 계획과 통제, 분석과 설계, 구현과 실행, 종료 조건 평가와 보고, 마무리로 나누고, 국내 수험 자료에서는 계획, 분석과 설계, 테스트 케이스·시나리오 작성, 수행, 결과 평가, 결함 추적과 관리처럼 나누기도 합니다. 어느 쪽이든 '계획하고, 설계하고, 실행하고, 평가한다'는 큰 흐름은 같습니다. 무엇을 어디까지 테스트할지 정하는 테스트 계획서, 실제 시험 내용을 담은 테스트 케이스와 시나리오, 실행 결과를 정리한 테스트 결과서가 이 과정에서 만들어지는 대표 산출물입니다.

목적에 따라 테스트를 부르는 이름도 있습니다. 일부러 장애를 일으켜 제대로 복구되는지 보는 회복 테스트, 외부 침입으로부터 시스템을 지키는지 보는 안전(보안) 테스트, 처리 능력 이상의 부하를 주어 어떻게 버티는지 보는 강도 테스트, 응답 시간과 처리량을 재는 성능 테스트, 내부 논리 구조와 코드의 복잡도를 평가하는 구조 테스트, 고친 뒤 원래 잘 되던 기능이 망가지지 않았는지 다시 보는 회귀 테스트, 새 시스템과 기존 시스템에 같은 데이터를 넣어 결과를 비교하는 병행 테스트가 대표적입니다.

이렇게 기준이 여러 개라는 것을 알면 문제를 읽을 때 덜 헷갈립니다. 같은 테스트라도 레벨로는 시스템 테스트이면서, 기법으로는 블랙박스이고, 목적으로는 성능 테스트일 수 있습니다. 문제가 어떤 기준으로 묻고 있는지를 먼저 확인하는 습관이 중요합니다.

실무에서는 이렇게 쓰여요

주문 수량 입력란의 테스트 케이스

주문 수량은 1~99개까지 허용됩니다. 동등 분할로는 유효 묶음(1~99)에서 50, 무효 묶음에서 -3과 150을 고릅니다. 경계값 분석으로는 유효 경계와 바로 바깥 값인 0, 1, 99, 100을 추가합니다. 각 케이스에는 '50 입력 시 주문 가능', '100 입력 시 수량 초과 안내'처럼 예상 결과를 실행 전에 적어 둡니다.

조건은 다 채웠는데 분기는 못 채운 경우

'나이가 20 이상이고 회원이면 할인'이라는 if 문을 (25세, 비회원)과 (15세, 회원)으로만 테스트했습니다. 두 조건이 각각 참과 거짓을 한 번씩 가졌으니 조건 커버리지는 채웠지만, 전체 결과는 두 번 다 '할인 없음'이라 할인되는 쪽 분기는 한 번도 실행되지 않았습니다. (30세, 회원) 케이스를 더해야 결정 커버리지도 채워집니다.

회원가입 흐름을 시나리오로 묶기

회원가입을 '약관 동의 → 정보 입력 → 이메일 인증 → 첫 로그인' 순서의 시나리오로 묶었습니다. 각 단계는 이미 만들어 둔 테스트 케이스를 가져다 쓰되, 앞 단계에서 입력한 이메일로 인증 메일이 실제로 가는지처럼 단계 사이의 연결을 함께 확인합니다. 인증 링크가 만료되는 예외 흐름은 별도 시나리오로 만들었습니다.

총정리 목차로