반응형

출처: The Art of Software Testing, G. J. Myers, 1979, 7Debugging, 페이지 130~146

 

프로그램 디버깅은 "성공적인(이상 징후 발견)" 테스트케이스 실행 후에 수행되는 활동이다. 디버깅 프로세스가 에러 존재에 대한 의심/징후로부터 출발하게 되며, 아래의 두 부분으로 구성된다.

(1) 프로그램 내 의심 에러의 정확한 본질(nature)과 위치(location)를 알아낸다.
(2) 에러를 고친다.

 위 두 가지 중 첫 번째 활동이 아마도 디버깅 문제(노력) 95%를 차지할 것이다.

 

마구잡이 디버깅(DEBUGGING BY BRUTE FORCE)

가장 일반적인 프로그램 디버깅 방법은 다소 비효율적인 마구잡이(brute force)" 방법이다. 이 방식은 생각이 거의 필요하지 않고 정신적 부담이 가장 적기 때문에 인기가 있지만, 대개는 가장 비효율적이고 성공적이지 않은 방법이다.

 

마구잡이 방법은 적어도 세 가지 범주로 나뉜다.

(1) 스토리지 덤프(storage dump)를 사용한 디버깅

(2) 프로그램 여기저기에 프린트문(print starement)을 흩뿌리는 방식의 디버깅

(3) 자동 디버깅 도구를 사용한 디버깅

 

스토리지 덤프(8진법 또는 16진법 형식으로 스토리지를 있는 그대로 표시) 분석을 통한 디버깅은 아마도 가장 비효율적인 방법일 것이며, 아래와 같은 문제가 있다.

1.     스토리지 위치와 소스 프로그램의 변수 간의 상응 관계를 찾기가 어렵다.

2.     많은 양의 데이터를 마주치게 되는데, 이 중 대부분이 관련 없는 데이터이다.

3.     덤프는 프로그램의 정적인 그림(, 한 순간의 프로그램 상태)이다. 그러나 에러를 찾으려면 대개 프로그램의 동적인 모습(, 시간 경과에 따른 상태 변화)을 연구해야 한다.

4.     덤프가 정확히 에러 시점에서 생성되는 경우가 드물다. 따라서 덤프는 에러 시점의 프로그램의 상태를 보여주지 않으며, 에러 시점과 덤프 시점 사이에 프로그램이 취한 액션으로 인해 필요한 단서가 가려질 수 있다.

5.     스토리지 덤프 분석을 통해 에러의 원인을 찾는 법을 기술한 방법론이 드물다(많은 프로그래머가 멍한 눈으로 덤프를 바라보며 마법처럼 에러가 스스로 모습을 드러내기를 기대한다).

 

두 번째로 변수 값이 드러나게 하기 위해 실패한 프로그램 전체에 프린트문을 흩뿌리는 방법도 그다지 나을 것이 없다. 프로그램의 동적인 모습을 볼 수 있고, 더 쉽게 소스 프로그램에 상응하는 정보를 검사 할 수 있다는 점에서 덤프를 사용하는 것보다 우수하지만, 이 방법도 아래와 같은 여러 단점이 있다.

1.     디버깅하는 문제에 대해 생각(사고)하도록 장려하기 보다는 대체로 운에 맡기고 무작정 해 보는 방법(hit-or-miss method)이다.

2.     분석해야 할 많은 양의 데이터를 생성하는 결과를 낳을 수 있다.

3.     프로그램에 변경을 가하는 방법인데, 이러한 변경이 에러를 가리거나, 중요한 타이밍 관계를 변경하거나, 또는 프로그램에 새로운 에러를 넣을 수 있다.

4.     작은 프로그램이라면 사용이 가능하지만, 대규모 프로그램/시스템에서 사용하는 경우 그 비용이 막대할 수 있다. 더구나 특정 유형의 프로그램(, 운영체제, 프로세스-제어 프로그램)에서는 적용 불가능한 경우가 많다.

 

세 번째의 자동 디버깅 도구를 사용하는 방법은 두 번째 범주와 유사하지만 프로그램 자체를 변경하는 대신 프로그래밍 언어(또는 대화형 디버깅 도구)의 디버깅 기능(features)을 사용하여 프로그램 동작을 분석한다. 흔히 사용되는 언어 기능으로 명령문 실행/서브루틴 호출/지정된 변수의 변화에 대한 추적(프린트)을 생성하는 기능이 있다. 일반적으로 디버깅 도구가 "중단점(break-points)" 설정 기능을 제공하는데, 이 기능은 특정 명령문(statement)이 실행되거나 또는 특정 변수가 변경될 때 프로그램을 일시 중지하고 프로그래머가 단말기에서 프로그램의 현재 상태를 검사 할 수 있도록 허용한다. 반복하지만, 이것도 막연히 시도 해 보는 방법(hit-or-miss method)이며 종종 과도한 양의 관련 없는 데이터를 생성하는 결과를 낳는다.

 

이러한 마구잡이식 디버깅의 일반적인 문제점은 이 방법들이 사고(thinking) 프로세스를 무시한다는 것이다. 마구잡이 방법은 (1) 다른 모든 방법이 실패하였을 경우 또는 (2) 후속 섹션에서 설명하는 사고 프로세스를 보완하는(대체가 아님) 경우에만 사용이 권장된다.

 

귀납법에 의한 디버깅(DEBUGGING BY INDUCTION)

주의 깊은 사고를 통해서, 심지어 컴퓨터 근처에 가까이 가지 않고도, 대부분의 에러 위치를 찾을 수 있다는 주장이 있다. 그러한 사고 프로세스의 하나가 귀납법(induction)으로, 특정 상세 사항에서 전체로 진행하는 방법이다. , 단서(하나 또는 하나 이상의 테스트케이스 결과로 나타나는 에러 징후)에서 시작하여 이 단서들 간의 관계를 찾다보면, 종종 에러 발견으로 이어질 수 있다.

 

1. 관련된 데이터를 찾는다.
프로그램을 디버깅 할 때 범하는 주요 실수는 문제에 대한 모든 가용한 데이터/증상을 고려하는데 실패하는 것이다. 첫 번째 단계는 프로그램이 무엇을 올바르게 수행하고 또 무엇을 잘못 수행하는지(, 우리가 에러가 존재한다고 믿게 만드는 증상)에 대해 알려진 모든 것을 열거하는 것이다. 만약 유사한, 하지만 동일하지 않은, 테스트케이스에서는 해당 증상이 나타나지 않는다면 이것이 추가적인 유용한 단서가 된다.

 

2. 데이터를 구조화(체계화)한다.
귀납법이 특정 사항에서 일반 사항으로 진행한다는 점을 기억하면서, 두 번째 단계는 패턴을 관찰 할 수 있도록 관련 데이터를 구조화한다. 특히 중요한 것은 모순(contradictions)’을 찾아내는 것이다(, "에러가 고객 증거금 계정에 미지불액이 없을 경우에만 발생한다").

 

가용한 데이터를 구조화하기 위해 그림 7.2와 같은 양식을 활용할 수 있다. "What" 칸에는 일반적인 증상을 나열하고, "Where" 칸에는 어디에서 증상이 관찰되었는지 설명한다. "When" 칸에는 증상이 발생한 시간에 대해 알려진 모든 것을 나열하고, "To what extent" 칸에는 증상의 범위와 중요도를 기술한다. "is" "is not" 열은 모순을 기술하며, 이 모순이 결국 에러에 대한 가설로 이어진다.

 

3. 가설을 세운다.
다음 단계는 단서들 간의 관계를 연구하고, 단서들의 구조에서 보이는 패턴을 사용하여 에러의 원인에 대한 하나 또는 하나 이상의 가설을 고안한다. 만일 가설을 고안 할 수 없다면 더 많은 데이터가 필요하며, 추가 테스트케이스를 개발하고 실행하여 데이터를 얻는다. 여러 가설이 가능해 보인다면 가장 가능성 있는 것을 먼저 선택한다.

 

4. 가설을 증명한다.
이 시점에 범하는 주요 실수가, 디버깅이 대개 여러 압박 하에 수행되다 보니, 증명 단계를 건너 뛰는 것이다. , 성급한 결론을 내리고 바로 문제 수정을 시도하는 것이다. 그러나 진행에 앞서 가설의 합리성을 입증하는 것이 필수적이다. 그렇지 않으면 문제의 증상만 수정되거나 또는 문제의 일부만 수정되는 결과로 이어진다. 가설은 원래의 단서 또는 데이터와 비교를 통해 입증되며, 이 가설이 단서들의 존재를 완전하게 설명하는지를 확인해야 한다. 만약 그렇지 못한 경우라면 가설이 유효하지 않거나, 가설이 불완전하거나, 또는 여러 에러가 존재하는 것이다.

 

 

) 4장의 시험 채점 프로그램 (52~53 페이지)
MTEST는 객관식 시험을 채점하는 프로그램으로, 그림 4.4와 같은 80-문자 레코드를 포함하는 OCR 파일을 입력으로 받는다. 첫 번째 레코드는 제목을 담고 있으며, 이것이 각 출력 보고서의 제목으로 사용된다. 그 다음 레코드 세트는 시험의 정답을 기록하는 레코드들이며, 각 레코드의 마지막 문자가 "2"로 표시된다. 이 세트의 첫 번째 레코드는 시험 문제 수를 1~3번째 열에 나타내며(1~999 범위 값), 10~59번째 열에는 문제 1~50번에 대한 정답을 기록한다(모든 문자가 답으로 유효함). 후속 레코드의 10~59번째 열에는 문제 51~100번의 정답, 101~150번의 정답, … 등이 기록된다.
 
세 번째 레코드 세트는 각 학생의 답안을 기록하며, 각 레코드의 80번째 열에 "3"을 표시한다. 각 학생에 대해서 첫 번째 레코드가 학생의 이름 또는 번호를 1~9번째 열에 표시한다(어떤 문자든 가능). 10~59번째 열에는 질문 1~50번에 대한 학생의 답을 기록한다. 시험에 50개 이상의 문항이 있는 경우 이 학생의 후속 레코드의 10~59번째 열에 문제 51-100, 101-150, …의 답이 기록된다. 최대 학생 수는 200명이다.
 
출력 레코드는 아래의 4가지가 존재한다.
(1) 학생 식별자(ID)에 따라 정렬된 보고서. 각 학생의 성적(정답 비율)과 순위를 보여줌
(2) 위와 유사한 보고서이지만 성적(grade)에 따라 정렬된 보고서
(3) 성적의 평균(mean), 중앙값(median), 표준편차(standard deviation)를 나타내는 보고서
(4) 시험 문제 번호에 따라 정렬된 보고서. 각 질문에 올바르게 답한 학생의 비율을 보여줌
 

 

디버깅의 간단한 예로 4장에서 기술된 시험 채점 프로그램에서 명백한 에러가 보고되었다고 가정하자. 모든 경우가 아닌 일부 경우에서 중앙값(median grade)이 부정확한 것 같다는 에러이다. 특정 테스트케이스 하나를 살펴보면 51명의 학생을 채점하였을 때 평균 점수는 73.2로 정확하지만 중앙값은 예상 값인 82 대신에 26이 출력되었다. 이 테스트케이스와 몇 개의 다른 테스트케이스의 결과를 조사하여 단서를 구조화한 모습이 그림 7.3과 같다.

 

다음 단계는 패턴과 모순을 찾는 과정을 통해 이 에러에 대한 가설을 세운다. 눈에 보이는 한 가지 모순은 이 에러가 홀수 학생을 사용하는 테스트케이스에서만 발생한다는 점이다. 이게 우연일 수도 있지만 샘플 수가 짝수인지 홀수인지에 따라 중앙값을 다르게 계산하기 때문에 이 점이 중요해 보인다. 또한 한 가지 이상한 패턴도 보이는데, 이러한 테스트케이스에서 계산된 중앙값이 항상 학생 수보다 작거나 같다는 점이다(26 <= 51 그리고 1 <= 1). 이 지점에서 우리가 갈 수 있는 한 가지 길은 51명의 학생을 포함한 테스트케이스를 다시 실행하는 것인데, 다만 학생들에게 이전과 다른 성적을 주고 이것이 중앙값 계산에 어떤 영향을 미치는지 보는 것이다. 실제 이렇게 해보니 중앙값이 여전히 ​​26이다. 따라서 "To what extent" 칸의 “is not” 열에 "중앙값이 실제 성적과 무관한 것으로 보인다."라는 내용을 채워 넣을 수 있다.

이 결과가 귀중한 단서를 제공하지만 이것 없이도 우리가 에러를 추측 할 수 있었을 지도 모른다. 가용한 데이터를 살펴보면 계산된 중앙값은 학생 수 절반이 다음 정수로 반올림된 것과 동일하다. 다시 말해서 성적이 정렬된 테이블에 저장되어 있다고 생각하면, 프로그램이 중간 학생의 성적이 아닌 그 엔트리 번호를 인쇄하는 것이다. 따라서 우리가 이 에러의 정확한 본질에 대한 확고한 가설을 가지게 된다. 다음은 코드를 검사하거나 또는 몇몇 추가 테스트케이스를 실행하여 이 가설을 입증해야 한다.

 

 

연역법에 의한 디버깅(DEBUGGING BY DEDUCTION)

그림 7.4에 나와 있는 연역적 프로세스는 제거(elimination)와 정제(refinemen) 과정을 사용하여  일반적인 이론/전제로부터 어떤 결론(에러의 위치)에 도달하도록 진행하는 프로세스이다. 예를 들어, 살인 사건에서 단서로부터 용의자를 유도하는 귀납적 프로세스와는 반대로, 이 방법은 한 무더기의 용의자들로부터 시작하여 제거(“정원사는 유효한 알리바이가 있다”) 및 정제(“범인은 빨간 머리이다”) 과정을 통해 집사가 범인이 틀림없다라는 결정을 내리는 것이다.  

 

1. 가능한 원인 또는 가설을 열거한다.

첫 번째 단계는 생각할 수 있는 모든 에러 원인을 나열한 목록을 개발하는 것이다. 이것들이 완전한 설명을 갖출 필요는 없다(우리가 가용한 데이터를 구조화하고 분석할 수 있도록 도와주는 단지 이론에 불과함).

 

2. 가능한 원인의 제거를 위해서 데이터를 사용한다.

모든 데이터를 주의 깊게 분석하여, 특히 모순을 찾아냄으로써(그림 7.2의 기법 활용), 가능한 여러 원인 중 하나를 제외하고 모두 제거한다. 만약 모두가 제거(탈락)되면 새로운 이론을 고안하기 위해 추가 데이터가 필요하다(, 추가적인 테스트케이스를 통해 확보). 만약 가능한 원인이 둘 이상 남아 있는 경우, 가장 가능성이 높은 원인을 주 가설(prime hypothesis)로 선택한다.

 

3. 남은 가설을 정제한다.

이 시점의 가능한 원인들이 정확성은 가지지만 에러를 콕 집어 낼 만큼 구체적이지는 않을 가능성이 높다. 따라서 다음 단계는 사용 가능한 단서를 사용하여 이론(, "파일의 마지막 트랜잭션 처리에 오류가 있다")이 더 구체적인 것이 되도록(, "버퍼에서 마지막 트랜잭션이 파일-종료-지시자와 오버레이 되었다") 정제한다.

 

4. 남은 가설을 증명한다.

이 중요한 단계는 귀납적 방법의 4 단계와 동일하다.

 

 

) 4장의 대화형 시스템에서 디버깅 명령어 (페이지 60~62)
DISPLAY 명령어는 단말기 창으로 스토리지(메모리) 내용을 출력하며 신택스가 그림 4.11과 같다. 대괄호는 선택가능한 피연산자 옵션을 나타낸다. 대문자는 피연산자 키워드이고, 소문자는 피연산자 값을 의미한다(, 실제 값으로 대체됨). 밑줄이 그어진 피연산자는 디폴트값이다(피연산자가 생략되었을 때 사용되는 값).
 
 
첫 번째 피연산자(hexloc1)는 출력할 스토리지의 첫 번째 바이트 주소를 지정한다. 이 주소는 길이가 1~616진수(0-9, A-F)로 표현하며, 명시되지 않는 경우 주소 0을 가정한다. 이 주소가 반드시 머신의 실제 스토리지 범위 내에 있어야 한다.
 
두 번째 피연산자는 출력할 스토리지 양을 지정한다. hexloc2가 명시되는 경우 출력할 스토리지 범위의 마지막 바이트 주소를 지정한다. 이 주소는 길이가 1~6 16진수이며, 반드시 시작 주소(hexloc1) 보다 크거나 같아야 한다. 또한 hexloc2가 머신의 실제 스토리지 범위 내에 있어야 한다. 피연산자 END가 쓰이면 머신의 마지막 바이트까지 스토리지 출력이 일어난다. 피연산자 bytecount는 출력할 스토리지 바이트 수를 지정하며(hexloc1에서부터 카운트를 시작), 16진법 정수(1~6 자리 수)로 표현한다. bytecounthexloc1의 합계가 실제 스토리지 크기에 1을 더한 값을 초과해서는 안되며, bytecount의 값은 최소 1 이상이어야 한다.
 
스토리지가 출력 될 때 단말기 화면에 보이는 출력 형식이 아래와 같다(아래 형식의 라인이 한 줄 또는 한 줄 이상 출력됨).



여기서 xxxxxx word1 16진수 주소이다. hexloc1의 값이나 출력 할 스토리지 양에 상관 없이 항상 워드의 인테그럴 넘버(워드의 첫 번째 바이트 주소가 4의 배수인 4-바이트 시퀀스)가 출력된다. 모든 출력 라인은 항상 4개의 워드(16바이트)를 포함하며, 출력 할 범위의 첫 번째 바이트가 첫 번째 워드 범위 내로 떨어지게 된다.
 
나타날 수 있는 에러 메시지는 다음과 같다.
“M1 유효하지 않은 명령어 신택스
“M2 요청된 스토리지가 실제 스토리지 한도를 초과
“M3 요청된 스토리지가 0 또는 음수 범위
 
아래는 이 명령어 사용 예이다.

 

예를 들어, 4장에서 논의한 DISPLAY 명령어의 기능 테스트를 시작한다고 가정하자. 원인-결과 그래프 방법으로 식별한 38개의 테스트케이스 중 4개의 테스트케이스를 실행하고자 하며, 입력 조건 설정의 일환으로 스토리지가 다음과 같이 초기화되었다.

첫 번째, 다섯 번째, 아홉 번째, ... 워드의 값이 0000

두 번째, 여섯 번째, ... 워드의 값이 4444

세 번째, 일곱 번째, … 워드의 값이 8888

네 번째, 여덟 번째, ... 워드의 값이 CCCC

 

, 워드의 첫 번째 바이트 주소(16진수)의 하위 자리 수(low-order digit) 값으로 각 워드가 초기화된다(예를 들어, 위치가 23FC, 23FD, 23FE, 23FF인 경우 초기화 값은 C이다).

 

그림 7.5는 테스트케이스, 예상 결과, 테스트 후 실제 결과를 보여준다. 테스트케이스의 실제 결과가 전부 예상 결과와 불일치 하기 때문에 명백히 문제가 있다. 먼저 첫 번째 테스트케이스와 관련된 에러 디버깅을 시작하기로 한다. 이 명령어는 디폴트값인 위치 0에서 시작하여 E(십진수로 14)개의 위치를 출력 할 것을 지시한다.

 

그러나 테스트 결과로 예상 못한 에러 메시지가 출력되었는데, 가능한 원인을 열거하면 아래와 같다.

1. 프로그램이 DISPLAY라는 단어를 받아들이지 않는다.

2. 프로그램이 마침표(.)를 받아들이지 않는다.

3. 프로그램이 첫 번째 피연산자로 디폴트를 허용하지 않는다(, 마침표 앞에 스토리지 주소가 나와야 한다고 여긴다).

4. 프로그램이 E를 유효한 바이트 카운트 수로 허용하지 않는다.

 

다음 단계는 식별된 원인 제거를 시도한다. 만약 남김없이 모든 원인이 제거되면 이전 단계로 돌아가 원인 목록을 확장해야 한다. 두 개 이상의 원인이 남은 경우, 추가적인 테스트케이스를 조사하여 단일 에러 가설에 이르도록 하거나, 또는 가장 가능성이 높은 원인을 골라 진행한다. 그림 7.5에 준비된 다른 테스트케이스를 참고하면, 두 번째 테스트케이스가 첫 번째 가설을 탈락시키고, 세 번째 테스트케이스는 결과가 정확하지는 않지만 두 번째와 세 번째 가설을 제거한다.

 

다음 단계는 남아 있는 네 번째 가설을 정제하는 것이다. 이것이 충분히 구체적으로 보일지 몰라도, 눈에 보이는 것 이상의 뭔가가 있다는(, 더 전반적인 에러 인스턴스처럼 보인다는) 직감이 들 수 있다. 프로그램이 특수 16진법 문자(A~F)를 인식 못하는 것 같다는 견해가 있으며, 다른 테스트케이스에는 이러한 문자가 없기 때문에 이 설명이 설득력 있게 들린다.

 

하지만 성급히 결론을 내리기 보다는 먼저 사용 가능한 모든 정보를 고려해야 한다. 네 번째 테스트케이스가 완전히 다른 에러를 나타낼 수도 있고, 아니면 현재 에러에 대한 단서를 제공 할 수도 있다. 우리 시스템에서 유효한 최고 주소가 7FFF라는 점을 감안할 때 네 번째 테스트케이스는 존재하지 않는 영역을 어떻게 표시하는지 확인하려는 의도가 있다. 테스트 결과 표시된 값이 쓰레기 값이 아니라 우리가 초기화한 값이라는 사실은 해당 명령어가 유효한  0~7FFF 범위 내의 무언가를 표시한다는 추정(가정)에 도달하게 한다. 만약 프로그램이 명령어 피연산자를 (프로그램 사양에 명시된 16진법이 아니라) 십진수로 취급한다면 이런 일이 발생할 수도 있다는 생각이 떠오르는 것이다. 이 추정은 세 번째 테스트케이스에 의해 입증된다. , 16진수 “11”(10진수로는 “17”) 보다 큰 다음 증분(네 개의 4-바이트 워드 시퀀스) 32바이트의 스토리지를 표시하는 대신 16바이트의 스토리지를 표시하는데, 이는 "11" 10진수로 취급한다는 가설과 일치한다. 따라서 프로그램이 bytecount 및 스토리지 주소 피연산자 그리고 출력 목록 상의 스토리지 주소를 10진수로 간주한다는 정제된 가설이 도출된다.

 

마지막 단계는 이 가설을 증명하는 것이다. 네 번째 테스트케이스에서 “8000” 10진수로 해석하는 경우 그에 상응하는 16진수 값이 “1F40”이며, 이것이 그림 7.5에 보여진 테스트 결과로 이어진다. 추가적인 입증을 위해 두 번째 테스트케이스를 살펴보자. 프로그램이 “21”“29”10진수로 받아들인 경우 스토리지 주소 15~1D를 출력하게 될 것이고, 이는 테스트케이스 결과(에러가 있는 결과)와 일치한다. 따라서 우리가 거의 확실하게 에러 위치를 찾은 것이다. , 프로그램이 피연산자를 10진수 값으로 가정하고 스토리지 주소를 10진수 값으로 출력한다(프로그램 사양과 불일치). 더구나 네 개의 테스트케이스 모두 이 에러로 인해 잘못된 결과가 생성되는 것으로 보인다. 약간의 사고를 통해 우리가 에러를 찾을 수 있었으며, 언뜻 보기에는 관련 없는 것으로 보이는 다른 세 가지 문제도 해결하였다.

 

이 에러가 프로그램의 두 군데(입력 명령어를 해석하는 부분과 출력 목록에 스토리지 주소를 인쇄하는 부분)에서 모습을 드러낼 가능성이 있음에 주의한다. 프로그램 사양을 제대로 숙지하지 못해 발생했을 가능성이 높은 이 에러는 프로그래머가 직접 자신의 프로그램을 테스트해서는 안된다는 주장을 뒷받침한다. 만약 이 에러를 만든 프로그래머가 테스트케이스 또한 설계하였다면, 테스트케이스 작성에서 똑같은 실수를 할 가능성이 높다. 피연산자가 10진수 값이라는 가정 하에 계산된 프로그래머의 예상 결과는 그림 7.5에 보여지는 것과는 다를 것이며, 따라서 이 에러를 알아채지 못하고 넘어갈 수도 있다.  

 

 

역추적에 의한 디버깅(DEBUGGING BY BACKTRACKING)

작은 프로그램에서 에러 위치를 찾는데 효과적인 방법은 부정확한 결과를, 로직이 잘못된 지점을 발견 할 때까지, 프로그램 로직을 통해 역추적하는 것이다. , 잘못된 결과가 생성된(, 출력된) 프로그램 지점을 시작점으로 하여, 이 지점에서 관찰된 출력에 기반하여 프로그램 변수의 값이 무엇이었을지를 추론해 나간다. 이 지점에서부터 머리속으로 프로그램을 거꾸로 실행함으로써, 그리고 "이 지점에서 프로그램의 상태(, 변수 값)가 이렇다면 여기까지의 프로그램 상태는 이러했을 것이다라는 if-then 로직을 반복적으로 적용함으로써, 우리가 에러의 위치를 빠르게 집어낼 수 있다.

 

 

테스팅에 의한 디버깅(DEBUGGING BY TESTING)

"사고를 통한" 디버깅 방법 중 마지막은 테스트케이스를 사용하는 것이다. 우리가 디버깅과 테스팅을 별개로 구분하였기 때문에 이 방법이 어쩌면 조금 이상하게 들릴지 모르는데, 두 가지 유형의 테스트케이스를 고려하는 것을 말한다. 테스팅을 위한 테스트케이스는 그 목적이 이전에 발견되지 않은 에러를 드러내는 것이고, 반면 디버깅을 위한 테스트케이스의 목적은 의심 에러 위치를 찾아내는데 유용한 정보를 제공하는 것이다. 이 두 가지의 차이점은 테스팅을 위한 테스트케이스는 "무거운" 경향이 있고(적은 수의 테스트케이스에서 많은 조건을 다루려고 시도 함), 반면 디버깅을 위한 테스트케이스는 "가볍다"(각 테스트케이스에서 오로지 하나의 조건 또는 몇 가지 조건만을 다루고자 함).

 

달리 말하면 이 방법은 의심되는 에러의 증상이 발견 된 후 원래 테스트케이스의 여러 변형(variants)을 작성함으로써 에러를 정확히 집어내려는 시도이다. 사실 이 방법은 완전히 독립적인 방법은 아니며, 귀납적 방법 또는 연역적 방법과 함께 사용하는 경우가 많다.

반응형

+ Recent posts