맞는지 확인하기 - 예시 입력과 기대 출력으로 검사
이 장에서 배우는 것
프로그램이 오류 없이 끝났다고 해서 원하는 일을 했다는 뜻은 아니다. 단어를 세는 프로그램이 빈 문장에서 단어가 하나 있다고 답해도 Python은 그 답을 계산 결과로 받아들인다. 틀렸다고 알려 주려면 사람이 먼저 올바른 결과를 정해야 한다. 이 장에서는 예시 입력과 기대 출력을 나란히 적고, 둘이 일치하는지 코드로 확인한다.
앞 장에서 고쳐 달라고 요청할 때 증상과 재현 방법, 기대 결과를 구분했다. 이제 그 기대 결과를 매번 다시 확인할 수 있는 검사로 남긴다. AI가 코드를 바꾼 뒤에는 같은 검사를 실행하고, 바뀐 줄을 비교하는 차이 검토(diff)도 함께 한다. 검사 결과와 변경 내용을 서로 다른 증거로 삼는 습관이다.
- 눈으로 결과를 살피는 일과 코드로 결과를 비교하는 일의 차이를 이해한다.
assert로 실제 결과와 기대 결과가 같은지 확인한다.- 빈 입력, 0, 아주 큰 값이 자신의 프로그램에서 무엇을 뜻하는지 정한다.
- AI에게 검사 사례를 먼저 작성하게 하고, 그 기대값을 직접 검토한다.
- 단어 개수 세기 함수와 검사 사례 표를 한 파일에 두고 통과와 실패를 출력한다.
문제 상황
모임 안내문을 정리하면서 문장에 들어 있는 단어 수를 세는 작은 프로그램을 만들었다고 하자. AI에게 부탁한 코드를 실행하니 “오늘 함께 걷자”의 결과로 3이 나왔다. 눈으로 보아도 맞는다. 그래서 다른 안내문에도 사용하기 시작했다.
그런데 빈 메모를 넣었을 때 1이 나오고, 단어 사이에 공백을 두 번 넣으면 수가 늘어난다. 줄을 바꾼 문장은 제대로 나뉘지 않는다. 처음 살펴본 문장은 우연히 코드가 처리할 수 있는 모양이었던 셈이다. 한 번 맞은 결과만으로는 다른 입력도 맞게 처리하는지 알 수 없다.
여기서 바로 “공백 문제를 고쳐 줘”라고 요청하면 무엇을 고쳤는지 판단하기 어렵다. 먼저 단어를 세는 규칙을 정해야 한다. “잘 세기”는 사람마다 다르게 이해할 수 있지만, “빈 문자열의 결과는 0”이라는 문장은 실행 결과와 바로 비교할 수 있다.
단어는 공백으로 나뉜 덩어리로 센다. 공백이 여러 개 이어져도 구분 한 번으로 취급한다. 탭과 줄바꿈도 구분으로 취급한다. 빈 문자열과 공백뿐인 문자열의 결과는 0이다. 문장부호는 따로 떼지 않는다.
이 규칙에서는 “안녕, 친구야”가 두 단어다. 쉼표를 별도 단어로 세지 않는다. 한국어의 문법적 단어를 분석하는 기능도 만들지 않는다. 지금 만드는 것은 정해진 공백 규칙에 따라 덩어리 수를 세는 프로그램이다. 범위를 이렇게 밝혀야 사람과 AI가 같은 결과를 목표로 삼을 수 있다.
눈으로 확인할 것과 코드로 확인할 것
눈으로 확인하면 출력 문장이 읽기 좋은지, 안내가 빠지지 않았는지 살필 수 있다. 반면 숫자 여덟 개를 이전 실행과 일일이 비교하는 일은 반복할수록 번거로워진다. 코드로 확인하면 같은 입력에 같은 기대값을 반복해서 적용할 수 있다. 이런 확인을 검사 또는 테스트(test)라고 한다.
검사에는 세 가지가 필요하다. 프로그램에 넣을 입력, 사람이 정한 기대 결과, 실행해서 얻은 실제 결과다. 기대 결과는 실행 결과를 본 다음 그대로 베껴 적는 값이 아니다. 원하는 동작의 규칙을 보고 먼저 정하는 값이다.
| 방법 | 확인하기 좋은 것 | 주의할 점 |
|---|---|---|
| 눈으로 확인 | 출력의 표현, 줄 배치, 읽기 쉬움 | 지난번 결과와 작은 차이를 놓칠 수 있다. |
| 코드로 확인 | 정해 둔 입력의 결과가 기대값과 같은지 | 기대값 자체가 틀렸으면 잘못 판단한다. |
| 바뀐 줄 비교 | 요청과 관련된 부분이 어떻게 달라졌는지 | 그럴듯한 변경도 실행 결과와 함께 확인한다. |
이 장에서는 숫자 결과를 코드로 확인하고, 통과와 실패 안내는 눈으로 읽는다. 검사가 모두 통과하더라도 준비하지 않은 입력까지 확인한 것은 아니다. 통과는 “이 파일에 적힌 사례에서는 기대한 결과가 나왔다”는 뜻으로 읽어야 한다.
assert로 기대 결과 적기
assert는 뒤에 적은 조건이 맞는지 확인하는 Python 문법이다. 조건이 맞으면 다음 줄로 넘어간다. 틀리면 AssertionError라는 오류를 낸다. 이 오류 이름은 적어 둔 조건이 성립하지 않았다는 뜻이다.
actual = 3
expected = 3
assert actual == expected
print("통과")
통과
actual은 실제 결과, expected는 기대 결과를 담는 이름이다. =는 오른쪽 값을 왼쪽 이름에 담는다. ==는 양쪽 값이 같은지 비교한다. 이름을 이렇게 나누어 적으면 어느 값을 확인하는지 읽기 쉽다. 조건이 틀렸을 때 보여 줄 설명은 쉼표 뒤에 적을 수 있다.
actual = 4
expected = 3
try:
assert actual == expected, "기대 3, 실제 4"
except AssertionError as error:
print(f"실패: {error}")
실패: 기대 3, 실제 4
try 안의 코드를 실행하다 지정한 오류가 생기면 except 아래로 이동한다. 여기서는 비교가 실패한 이유를 출력하고 실행을 이어 간다. as error는 그 오류를 error라는 이름으로 받아 둔다. 글 앞에 붙인 f는 중괄호 속 값을 글 안에 넣으라는 표시다.
검사 하나만 실행할 때는 실패한 자리에서 멈춰도 된다. 하지만 여러 사례를 살펴볼 때는 첫 실패 뒤에 있는 결과도 보고 싶다. 완성 코드에서는 비교 실패만 받아서 기록하고 다음 사례를 실행한다. 철자가 틀린 이름을 사용한 경우처럼 다른 종류의 오류는 그대로 드러나게 둔다.
경계값을 고르고 기대값을 정하기
경계값은 처리 방식이 달라지거나 실수하기 쉬운 끝자락의 값이다. 숫자를 다루는 프로그램이라면 0이나 허용되는 최댓값이 후보가 된다. 문자열, 즉 글자들의 나열을 다루는 프로그램이라면 글자가 하나도 없는 경우와 아주 긴 경우가 후보가 된다. 경계값 목록을 그대로 외우기보다 입력의 종류와 규칙을 보고 골라야 한다.
이번 함수는 문자열만 입력받는다. 따라서 숫자 0을 그대로 넣는 대신 글자 "0"을 검사한다. 숫자 0과 글자 “0”은 다른 값이다. 따옴표가 있으면 글자로 취급한다. 글자 “0”에는 공백으로 나뉜 덩어리가 하나 있으므로 기대 결과는 1이다. 단어 수의 결과가 0인 경우는 빈 문자열과 공백뿐인 문자열에서 따로 확인한다.
| 사례 | 입력 | 기대 결과 | 확인할 규칙 |
|---|---|---|---|
| 보통 문장 | 오늘 함께 걷자 | 3 | 기본적인 공백 구분 |
| 빈 입력 | 글자가 없는 문자열 | 0 | 없는 단어를 세지 않음 |
| 공백만 | 공백 세 개 | 0 | 구분 문자만 있는 경우 |
| 연속 공백 | 앞뒤와 단어 사이에 여러 공백 | 2 | 빈 덩어리를 세지 않음 |
| 탭과 줄바꿈 | 탭과 줄바꿈으로 나눈 세 덩어리 | 3 | 여러 종류의 공백 구분 |
| 글자 0 | 0 | 1 | 내용이 0이어도 한 덩어리 |
| 문장부호 | 안녕, 친구야 | 2 | 쉼표를 별도로 세지 않음 |
| 긴 입력 | “낱말 ”을 10,000번 반복 | 10000 | 긴 입력에서도 같은 규칙 적용 |
긴 입력은 실제 단어를 만 개 손으로 쓸 필요가 없다. Python에서 문자열에 정수를 곱하면 그 횟수만큼 반복한 문자열을 만든다. "낱말 " * 10000은 끝에 공백이 붙은 “낱말 ”을 만 번 이어 붙인다. 공백은 단어로 세지 않으므로 기대 결과는 10000이다.
이 사례는 긴 입력에서도 결과가 맞는지 확인한다. 처리 속도가 충분히 빠른지 측정하는 검사는 아니다. 이번에는 시각이나 실행 시간을 출력하지 않는다. 같은 코드를 같은 방식으로 실행하면 결과 안내도 같게 유지한다.
AI에게 검사 코드를 먼저 요청하기
구현을 받은 다음 검사를 덧붙일 수도 있지만, 이번에는 검사 사례와 기대값부터 요청한다. 구현은 원하는 일을 수행하는 코드라는 뜻이다. 먼저 원하는 결과를 검토하면 AI가 제안한 구현 방식에 맞춰 규칙을 바꾸는 일을 줄일 수 있다.
공백으로 구분된 단어 개수를 세는 Python 프로그램을 만들려고 한다. 구현 코드는 아직 쓰지 말고 검사 사례 표부터 작성해 줘. 입력은 문자열로 한정한다. 빈 문자열과 공백뿐인 문자열은 0, 연속 공백은 구분 한 번으로 처리한다. 탭과 줄바꿈도 구분으로 처리하고 문장부호는 떼지 않는다. 글자 “0”과 아주 긴 입력도 포함해 줘. 각 기대값의 이유를 설명해 줘.
답을 받으면 기대값을 직접 읽는다. 보통 문장의 단어는 손으로 세고, 빈 문자열의 결과는 규칙과 대조한다. 긴 입력은 반복 횟수와 덩어리 수를 연결해 판단한다. AI가 제안한 표라고 해서 기대값까지 맞는 것은 아니다. 표가 규칙과 맞는 것을 확인한 뒤 다음 요청을 보낸다.
확인한 사례를 코드 안의 목록으로 옮겨 줘. 각 항목은 사례 이름, 입력 문자열, 기대 개수로 구성해 줘. count_words 함수와 assert를 사용하는 작은 검사 실행기를 main.py 하나에 작성해 줘. 비교 실패는 기대값과 실제값을 출력한 뒤 다음 사례로 진행하고, 그 밖의 오류는 숨기지 마. 마지막에 전체·통과·실패 개수를 출력해 줘. 입력 대기, 외부 패키지, 파일 읽기, 네트워크 연결은 사용하지 마.
받은 코드에서는 검사 목록의 기대값을 다시 본다. 구현과 검사를 한꺼번에 생성하면 두 부분이 같은 잘못된 생각을 공유할 수 있다. 예를 들어 빈 문자열을 1로 세는 함수와 기대값 1을 함께 만들면 검사는 통과한다. 기대값을 사람이 규칙과 대조하는 단계가 필요한 이유다.
수정을 요청한 뒤에도 기대값이 슬쩍 바뀌지 않았는지 변경 전후의 줄을 비교한다. 실패를 없애려고 기대값을 실제값에 맞추면 검사 목적이 사라진다. 규칙을 바꾸기로 결정한 경우에만 그 이유에 맞춰 기대값을 고친다. 이 장에서는 아래 코드를 실행해 결과를 확인하는 데 집중한다.
완성 코드
다음 코드를 main.py로 저장한다. 빈 줄을 포함해 56줄이며 외부 패키지는 사용하지 않는다. 사례 목록이 코드 안의 검사 표 역할을 한다. 함수는 이름을 붙여 다시 실행할 수 있도록 묶어 둔 코드다. count_words는 개수를 계산하고, run_checks는 사례를 하나씩 확인한다.
def count_words(text):
words = text.split()
return len(words)
def run_checks(cases):
passed = 0
failed = 0
for name, text, expected in cases:
actual = count_words(text)
try:
assert actual == expected, (
f"기대 {expected}, 실제 {actual}"
)
except AssertionError as error:
failed += 1
print(f"[실패] {name}: {error}")
else:
passed += 1
print(f"[통과] {name}: {actual}")
return passed, failed
def main():
cases = [
("보통 문장", "오늘 함께 걷자", 3),
("빈 입력", "", 0),
("공백만", " ", 0),
("연속 공백", " 작은 시작 ", 2),
("탭과 줄바꿈", "하나\t둘\n셋", 3),
("글자 0", "0", 1),
("문장부호", "안녕, 친구야", 2),
("긴 입력", "낱말 " * 10000, 10000),
]
print("단어 개수 검사")
print(f"검사 사례: {len(cases)}개")
print()
passed, failed = run_checks(cases)
print()
print(f"전체: {len(cases)}개")
print(f"통과: {passed}개")
print(f"실패: {failed}개")
if failed == 0:
print("결과: 준비한 검사를 모두 통과했다.")
else:
print("결과: 실패한 사례를 확인해야 한다.")
main()
줄별 해설
줄 번호는 빈 줄도 포함한다. Python은 줄 앞의 빈칸인 들여쓰기로 같은 묶음에 속하는 코드를 구분한다. 함수 안의 줄은 네 칸, 반복문이나 조건문 안으로 더 들어간 줄은 거기서 다시 네 칸을 들여쓴다. 빈 줄은 묶음 사이를 읽기 쉽게 나누며 계산하지 않는다.
| 줄 | 코드의 부분 | 하는 일 |
|---|---|---|
| 1 | count_words 정의 | 문자열을 text라는 이름으로 받아 처리할 함수를 만든다. |
| 2 | text.split() | 공백을 기준으로 나눈 덩어리들을 words에 담는다. |
| 3 | return len(words) | 덩어리 개수를 함수의 결과로 돌려준다. |
| 4~5 | 빈 줄 | 함수 사이를 나눈다. |
| 6~8 | run_checks 정의와 초기값 | 사례 목록을 받고 통과와 실패 개수를 0에서 시작한다. |
| 9 | 빈 줄 | 준비와 반복 부분을 나눈다. |
| 10 | for 반복문 | 사례 하나의 세 값을 name, text, expected로 꺼낸다. |
| 11 | 실제 결과 계산 | 함수를 실행하고 반환한 수를 actual에 담는다. |
| 12 | 빈 줄 | 계산과 비교 부분을 나눈다. |
| 13~16 | try와 assert | 두 수를 비교하며, 다르면 기대값과 실제값을 담은 오류를 낸다. |
| 17~19 | except 부분 | 비교 실패를 받아 실패 개수를 늘리고 설명을 출력한다. |
| 20~22 | else 부분 | try에서 오류가 없으면 통과 개수를 늘리고 결과를 출력한다. |
| 23 | 빈 줄 | 반복과 반환 부분을 나눈다. |
| 24 | 두 값 반환 | 모든 사례를 확인한 뒤 통과와 실패 개수를 돌려준다. |
| 25~26 | 빈 줄 | 함수 사이를 나눈다. |
| 27~28 | main 정의와 목록 시작 | 전체 실행 순서를 묶고 검사 사례 목록을 만든다. |
| 29~36 | 검사 사례 여덟 개 | 이름, 입력, 기대 개수를 한 항목씩 적는다. |
| 37 | 목록 끝 | 대괄호를 닫아 사례 작성을 마친다. |
| 38 | 빈 줄 | 사례 준비와 출력 부분을 나눈다. |
| 39~41 | 시작 안내 | 제목과 사례 수를 출력한 뒤 빈 줄을 출력한다. |
| 42 | 빈 줄 | 안내와 검사 실행을 나눈다. |
| 43 | 검사 실행 | 검사를 실행하고 돌려받은 두 수를 각각 담는다. |
| 44 | 빈 줄 | 검사와 집계 출력을 나눈다. |
| 45~48 | 집계 출력 | 빈 줄 뒤에 전체·통과·실패 개수를 출력한다. |
| 49 | 빈 줄 | 집계와 최종 안내를 나눈다. |
| 50~53 | if와 else | 실패가 0인지에 따라 마지막 안내를 고른다. |
| 54~55 | 빈 줄 | 함수 정의와 실제 실행을 나눈다. |
| 56 | main() | 정의해 둔 전체 실행 순서를 시작한다. |
2줄의 split()은 괄호 안에 구분 문자를 적지 않았다. 이때 이어진 공백을 한 구분으로 다루고, 앞뒤 공백 때문에 빈 덩어리를 만들지 않는다. 탭과 줄바꿈도 구분한다. 빈 문자열이나 공백뿐인 문자열에서는 덩어리가 없는 목록을 돌려준다. 목록은 여러 값을 순서대로 담는 그릇이며 len은 그 안의 항목 수를 구한다.
28~37줄의 대괄호도 목록을 만든다. 각 항목의 소괄호는 세 값을 한 묶음으로 만든다. 이를 튜플(tuple)이라고 한다. 10줄에서는 한 묶음을 꺼낼 때마다 세 값을 각각의 이름에 담는다. 이름·입력·기대값의 순서는 모든 사례에서 같아야 한다.
33줄의 \t는 탭, \n은 줄바꿈을 문자열 안에 적는 표기다. 두 글자를 그대로 세라는 뜻이 아니다. 그 문자열을 함수에 넘기면 “하나”, “둘”, “셋” 사이에 탭과 줄바꿈이 들어 있는 상태로 처리한다.
18줄과 21줄의 += 1은 현재 수에 1을 더해 다시 담는 짧은 표기다. 20줄의 else는 try에서 오류가 없었을 때 실행된다. 52줄의 else는 if의 조건이 맞지 않을 때 실행된다. 어느 묶음과 같은 깊이로 들여썼는지 보면서 읽는다.
assert는 여기처럼 개발 중 결과를 검사할 때 사용한다. Python을 최적화 옵션인 -O와 함께 실행하면 이 검사가 생략될 수 있다. 이 장에서는 안내한 명령 그대로 실행한다. 사용자 입력을 반드시 거절해야 하는 조건을 처리할 때도 assert에만 맡기지 않는다.
실행 결과
main.py를 저장한 폴더에서 터미널에 다음 명령을 입력한다. 입력을 기다리는 단계 없이 검사 여덟 개를 실행하고 끝난다.
python3 main.py
단어 개수 검사
검사 사례: 8개
[통과] 보통 문장: 3
[통과] 빈 입력: 0
[통과] 공백만: 0
[통과] 연속 공백: 2
[통과] 탭과 줄바꿈: 3
[통과] 글자 0: 1
[통과] 문장부호: 2
[통과] 긴 입력: 10000
전체: 8개
통과: 8개
실패: 0개
결과: 준비한 검사를 모두 통과했다.
실패 안내도 한 번 확인한다. 30줄의 빈 입력 사례에서 마지막 숫자만 0에서 1로 잠시 바꾸고 실행한다. 함수는 여전히 0을 계산하므로 해당 줄은 다음과 같이 나온다.
[실패] 빈 입력: 기대 1, 실제 0
이때 마지막 집계는 통과 7개, 실패 1개가 된다. 실패 출력이 동작하는지 확인한 뒤 기대값을 다시 0으로 돌려놓고 전체를 실행한다. 이 실험에서 틀린 것은 함수가 아니라 일부러 바꾼 기대값이다. 실패 표시는 원인을 조사할 출발점이며 구현이 틀렸다는 판결은 아니다.
실무에서 자주 틀리는 것
공백 한 칸으로만 나누기
다음은 이번 규칙에 맞지 않는 코드다. split(" ")은 공백 한 칸을 구분 문자로 지정하므로 연속 공백 사이에 빈 항목이 생긴다. 탭과 줄바꿈도 이번에 원하는 방식으로 나누지 못한다. 짧은 사례를 직접 실행해 차이를 본다.
text = "작은 시작"
actual = len(text.split(" "))
print(actual)
3
이번 규칙에서는 구분 문자를 생략한 형태로 고친다. 수정한 줄을 확인한 다음 빈 입력, 연속 공백, 탭과 줄바꿈 사례를 포함한 전체 검사를 다시 실행한다.
text = "작은 시작"
actual = len(text.split())
assert actual == 2
print(actual)
2
실제 결과를 기대값으로 다시 쓰기
다음 검사는 실행은 되지만 결과의 옳고 그름을 가리지 못한다. 함수가 어떤 수를 반환하든 그 수를 기대값으로 삼기 때문이다. 두 이름이 같다는 사실만 확인하게 된다.
text = "작은 시작"
actual = len(text.split())
expected = actual
assert actual == expected
print("통과")
기대값은 규칙을 보고 별도로 정한다. 이 입력은 공백으로 나뉜 덩어리가 두 개이므로 2다. AI가 기대값을 이렇게 계산 결과에서 가져오지 않았는지도 검토한다.
text = "작은 시작"
expected = 2
actual = len(text.split())
assert actual == expected
print("통과")
아무 오류나 잡아서 통과라고 출력하기
다음 코드는 빈 입력의 실제 결과가 0인데 1과 비교한다. 비교가 실패해도 “통과”를 출력하므로 읽는 사람을 잘못 안내한다. 종류를 지정하지 않은 except는 다른 오류까지 넓게 받아 원인을 숨길 수도 있다.
try:
assert len("".split()) == 1
except:
print("통과")
비교 실패만 받을 때는 오류 이름을 적고 실제 실패로 안내한다. 다음 예시는 실패 출력을 살피기 위해 기대값 1을 그대로 둔다. 운영할 검사 목록에서는 규칙에 맞는 기대값 0으로 고쳐야 한다.
try:
assert len("".split()) == 1, "빈 입력은 0이어야 한다."
except AssertionError as error:
print(f"실패: {error}")
실패: 빈 입력은 0이어야 한다.
문자열 함수에 숫자 0을 바로 넣기
이번 함수는 문자열을 받기로 했다. 다음 코드는 숫자에도 문자열의 split 기능이 있다고 가정하므로 오류가 난다. 빈 입력을 검사하려고 숫자 0을 쓰는 것도 입력 종류를 혼동한 것이다.
text = 0
print(len(text.split()))
글자 “0”을 검사하려면 따옴표를 붙인다. 빈 입력은 ""로 별도 검사한다. 숫자도 받아야 하는 프로그램으로 바꾸려면 먼저 숫자를 어떤 규칙으로 처리할지 정하고 검사 사례를 추가해야 한다.
text = "0"
assert len(text.split()) == 1
assert len("".split()) == 0
print("통과")
한눈에 보기
| 항목 | 이 장의 선택 | 확인 질문 |
|---|---|---|
| 입력 범위 | 문자열 | 숫자와 글자를 구분했는가? |
| 기대 결과 | 규칙에서 먼저 정한 개수 | 실제 결과를 그대로 베끼지 않았는가? |
| 경계값 | 빈 입력, 공백만, 글자 0, 긴 입력 | 평소 예시와 다른 끝자락을 골랐는가? |
| 비교 | assert actual == expected | 실패 때 두 값을 읽을 수 있는가? |
| 실패 처리 | 비교 실패를 기록하고 계속 실행 | 다른 종류의 오류까지 숨기지 않는가? |
| 변경 확인 | 바뀐 줄 비교 후 전체 검사 재실행 | 기대값이 근거 없이 바뀌지 않았는가? |
| 통과의 의미 | 준비한 사례에서 기대값과 일치 | 확인하지 않은 입력까지 보장한다고 여기지 않는가? |
검사는 AI의 답을 읽는 또 하나의 방법이다. 요청문에 적힌 규칙이 사례로 옮겨졌는지 보고, 그 사례를 실행해 실제 결과와 대조한다. 다음에 코드를 바꾸더라도 같은 사례를 다시 실행하면 이전에 확인한 동작이 유지되는지 살필 수 있다.
연습 문제
- 완성 코드에 “앞뒤 공백”이라는 사례를 추가한다. 입력은
" 혼자 "다. 실행 전에 기대값과 이유를 적고, 실행 후 전체·통과·실패 개수를 확인한다. - 완성 코드의
text.split()을text.split(" ")으로 바꾸었다고 하자. 실패할 사례 네 개와 각각의 실제 결과를 먼저 예상한다. 실행 결과와 비교한 뒤 코드를 원래대로 돌린다. - “비 오는 날”의 결과가 3인 것을 확인한 사람이 “모든 문장을 제대로 센다”고 결론 내렸다. 아직 확인하지 않은 입력 두 개와 기대값을 제시하고, 이 결론을 어떻게 고쳐 말할지 적는다.
- AI에게 검사 사례를 먼저 요청하는 문장을 작성한다. 입력 범위, 단어를 나누는 규칙, 경계값, 기대값의 이유를 포함한다. 검사가 실패했을 때 기대값을 바로 바꾸면 안 되는 이유도 설명한다.
정답과 해설
-
기대값은 1이다. 앞뒤의 공백은 단어로 세지 않고 “혼자”만 한 덩어리로 센다. 아래 줄을 사례 목록 안에 추가한다. 다른 줄을 바꾸지 않았다면 전체 9개, 통과 9개, 실패 0개가 나온다.
case = ("앞뒤 공백", " 혼자 ", 1) name, text, expected = case actual = len(text.split()) assert actual == expected print(f"[통과] {name}: {actual}")목록에 옮길 항목은 첫 줄의 소괄호로 묶인 세 값이다. 목록 안에서는 다른 항목처럼 끝에 쉼표를 붙인다. 위 코드는 그 사례 하나를 따로 실행해 보는 형태다.
-
“빈 입력”은 실제 1, “공백만”은 실제 4, “연속 공백”은 실제 8, “탭과 줄바꿈”은 실제 1이 되어 실패한다. 연속 공백 사례에는 앞에 두 칸, 가운데 세 칸, 뒤에 두 칸이 있다. 공백 한 칸마다 나누면 빈 항목까지 생긴다. 긴 입력도 마지막 공백 때문에 실제 10001이 되어 실패하므로 실패 사례는 모두 다섯 개다. 수정한 줄을 원래대로 돌린 뒤 여덟 사례가 다시 통과하는지 확인한다.
-
예를 들어
""의 기대값은 0이고,"비\t오는\n날"의 기대값은 3이다. 앞의 입력은 빈 입력 처리를, 뒤의 입력은 탭과 줄바꿈 처리를 확인한다. 결론은 “공백 한 칸으로 나눈 ‘비 오는 날’에서는 기대한 3이 나왔다”라고 고칠 수 있다. 다른 사례는 별도로 실행해야 한다. -
다음과 같이 요청할 수 있다.
문자열에서 공백으로 나뉜 덩어리 수를 세려고 한다. 연속 공백, 탭, 줄바꿈은 구분으로 처리하고 빈 입력과 공백만 있는 입력은 0으로 센다. 문장부호는 떼지 않는다. 구현 전에 빈 입력, 글자 “0”, 긴 입력을 포함한 검사 사례와 기대값의 이유부터 작성해 줘.
실패는 실제값과 기대값이 다르다는 뜻이다. 어느 쪽이 잘못됐는지는 규칙을 보고 판단해야 한다. 실제값에 맞춰 기대값을 바꾸기만 하면 잘못된 구현도 통과할 수 있다. 입력과 규칙, 계산 과정, 기대값을 차례로 확인하고 근거가 있는 부분을 수정한다.