테스트와 검토 관문
80분 안팎
학습 목표
하네스가 단위·통합 검사 종료 코드를 확인한 뒤 검토 대기로 이동하게 구성합니다.
개념
검사 이름이 아니라 종료 결과를 봅니다
할 일 제목 검사에서 빈 문자열이 통과하는 결함을 고친 뒤 단위 검사와 연결 검사를 실행한다고 가정합니다. 터미널에 테스트라는 단어가 보이거나 명령 실행 로그가 생겼다는 사실만으로 다음 단계에 가면 안 됩니다. 실행한 프로그램의 종료 결과를 읽어야 합니다. 이번 실습은 고정된 두 검사 프로그램을 순서대로 호출하고 하나라도 실패하면 완료를 막습니다. 단위 성공 이후 연결 검사까지 통과한 후보만 검토 대기에 두며 사람의 현재 후보 승인과 구분합니다.
단위와 통합의 확인 범위가 다릅니다
단위 검사는 제목 정규화 함수에 공백을 넣고 빈 제목 거절을 확인합니다. 연결 검사는 그 제목으로 만든 할 일 객체가 완료 전이를 거친 뒤 기대 구조인지 비교합니다. 레슨 ZIP의 두 프로그램은 순수 Python 모형 fixture입니다. Java의 HTTP 상태와 서비스 연결을 검사했다는 뜻은 아닙니다. 미션에서는 기존 하네스 unittest를 먼저 실행하고 verify_app.py로 Java 회귀와 Python 참조 검사를 이어 실행합니다. 어떤 계약을 검사했는지 명령 이름보다 검사 내용과 사례로 설명합니다.
순차 관문은 첫 실패에서 멈춥니다
gate는 unit_check.py를 실행하고 0이면 integration_check.py를 실행합니다. 단위 종료 코드가 1이면 repair를 반환하고 통합 호출 횟수는 0이어야 합니다. 이미 잘못된 후보라는 근거가 있는데 다음 작업을 계속 실행할 필요가 없기 때문입니다. 두 검사를 독립적으로 한꺼번에 실행해 결과를 모두 보고 싶은 정책도 가능하지만 이번 계약은 순차 중단입니다. 테스트는 최종 상태뿐 아니라 호출 목록의 길이까지 확인합니다. 결과 문자열만 바꾸어 실패를 숨기는 수정은 호출 횟수 검사에서 드러납니다.
0과 미확인을 같은 값으로 취급하지 않습니다
이 검사 프로그램들은 성공일 때 정수 0, 실패일 때 다른 정수를 반환하도록 작성되었습니다. 프로그램을 시작하지 못한 OSError는 실행 미확인으로 표현하여 None을 반환하고 상태는 unverified입니다. None을 기본값 0으로 치환하면 실행되지 않은 검사가 통과로 바뀝니다. Python에서는 False와 0이 같다고 비교될 수 있으므로 type(code) is int도 확인합니다. 자료형이 틀린 시험 대역 결과는 repair로 판정합니다. 실제 subprocess의 returncode와 테스트가 주입하는 관찰 자료를 구분합니다.
출력 메시지는 종료 코드와 함께 보관합니다
UNIT OK라는 출력만으로 성공을 인정하면 출력 후 실패한 프로그램을 놓칩니다. 로컬 함수는 종료 코드와 사례 이름을 records에 남깁니다. 짧은 교육 함수는 전체 표준 출력을 반환하지 않지만 미션의 guarded_capture는 가린 stdout와 stderr를 로그에 저장하고 해시를 남깁니다. 앞 모듈의 필터를 제거하여 자세한 오류를 보려 하지 않습니다. 로그를 읽을 때는 실패 사례 ID, 종료 코드, 기대 결과를 우선 확인하고 원본 개인 정보나 자격 증명을 다시 넣지 않습니다.
명령 인수를 고정된 배열로 실행합니다
run에 전달하는 값은 python3와 검사 파일 이름으로 이루어진 배열입니다. 모델이 임의 문자열 명령을 제공하도록 열지 않습니다. subprocess.run은 출력 수집과 동기 실행을 담당하며 shell=True를 사용하지 않습니다. 프로그램 이름만 허용한 뒤 추가 인자를 받으면 명세와 다른 코드를 실행할 수 있으므로 배열 전체의 의미를 검토합니다. 미션은 기존 boundary.py가 등록한 명령만 사용합니다. 범용 Python 실행 권한을 새로 준다고 생각하지 않고 검토된 검사 파일의 내용까지 보호합니다.
검토 대기는 테스트 실패와 다릅니다
두 검사 결과가 0이어도 review가 없으면 review_pending입니다. 이때 다음 행동은 실패한 코드를 고치는 것이 아니라 현재 후보의 diff를 사람이 읽는 것입니다. reject면 repair이고 approve여도 revision이 다르면 review_pending입니다. 과거 후보 r1을 승인한 기록을 새 후보 r2에 재사용하지 않는 계약입니다. 상태가 모두 completed가 아니라는 이유로 같은 실패로 집계하면 대기와 결함이 섞입니다. 운영 기록에는 테스트 통과와 검토 대기를 두 항목으로 남깁니다.
후보 식별자는 무엇을 묶는지 정의합니다
레슨의 revision은 사람이 전달한 짧은 후보 이름입니다. 비교 논리를 익히는 fixture이므로 이름만으로 파일 변조를 찾는 구현은 없습니다. 미션은 정책의 읽기 목록 전체에 대해 파일 해시를 만들고 실제 검사 기록의 beforeHashes와 hashes를 현재 파일과 대조합니다. 단위 검사 이후 파일이 달라져 통합 검사만 새 후보를 검사했다면 두 결과를 한 승인으로 묶지 않습니다. 검토 fixture는 scope.json·state.json·handoff.md를 제외한 파일 해시 묶음을 가리킵니다. 인계 상태 기록은 구현 후보의 승인 대상과 분리합니다. 범위 명세가 승인 fixture의 보호 해시를 포함하므로 자기 참조를 피하려고 제외합니다. 실제 검사 전후 해시에는 scope.json도 포함되어 검사 중 변경은 거절합니다. 해시 일치는 내용을 검토했다는 증명이 아니라 동일 후보를 식별하는 수단입니다.
starter의 실패를 구체적으로 읽습니다
bash check.sh를 실행하면 test_unit_failure_stops_integration과 test_integration_failure_blocks_completion이 실패합니다. 첫 사례는 단위 실패 뒤에 두 번째 명령까지 호출된 것을, 둘째는 실패한 통합 결과에도 완료한 것을 찾습니다. gate.py의 TODO 조건에서 정수 0 이외의 값을 걸러 repair로 반환하도록 고칩니다. test_gate.py나 검사 기대값은 수정하지 않습니다. 실행 미확인과 현재 승인 사례는 처음부터 통과하므로 이 경로도 보존해야 합니다. 전부 거절하도록 바꾸면 정상 완료 검사가 실패합니다.
검사 대역으로 드물지만 중요한 경로를 만듭니다
test_gate는 실행 함수를 주입하여 정해진 종료 코드 배열을 순서대로 반환합니다. 실제 파일을 깨뜨리거나 실패 프로그램을 계속 남겨 둘 필요 없이 단위 실패와 통합 실패를 독립적으로 재현합니다. called 배열은 호출된 argv를 기록합니다. None 사례는 실행 환경 문제를, 오래된 승인 사례는 인계 자료 문제를 드러냅니다. 이 대역만으로 실제 subprocess 동작을 검증했다고 말하지 않습니다. test_real_checks가 제공된 두 프로그램을 실제 실행해 0과 0을 관찰하여 연결을 보완합니다.
테스트 없는 통과를 별도로 방지합니다
명령이 0으로 끝나더라도 검사 사례가 하나도 실행되지 않는 문제가 있을 수 있습니다. 미션의 verify_app.py는 이전 Maven XML 보고서를 지우고 새 보고서가 있으며 테스트 수가 양수인지 확인합니다. 실패·오류·건너뛴 항목이 있으면 통과로 인정하지 않습니다. 관문 함수가 종료 코드만 읽는다는 설명은 이 신뢰하는 검사 파일이 요구 계약을 검사한다는 전제를 포함합니다. 빈 파일을 검사 명령으로 바꾸어도 상태표가 보호할 것이라고 기대하지 않습니다. 보호 해시와 diff 검토가 함께 필요합니다.
완료 판단의 증거를 인계합니다
제출에는 단위 실패 후 미호출, 통합 실패 후 repair, 모두 통과 후 검토 대기, 현재 승인 후 completed의 네 경로를 설명합니다. 미션에서 통과한 로그의 파일 해시와 현재 후보를 연결하고 사람이 승인한 실제 기록과 교육용 승인 fixture를 혼동하지 않습니다. 모의 승인으로 루프 연결은 확인했지만 실제 사람의 최종 승인은 별도입니다. 검사 기준을 만들고 채택본을 보호하는 넓은 방법은 더 읽기로 보냅니다. 이번 산출물은 종료 결과를 소비하는 관문 코드와 그 반례입니다.
따라하기
종료 코드 관찰 모형
이 단계는 종료 결과 판정만 분리한 모형입니다. 실제 두 프로그램 호출은 ZIP에서 python3 gate.py로 확인합니다.
codes = [0, 2]
print('review_pending' if all(type(c) is int and c == 0 for c in codes) else 'repair')실행 결과
repair
미확인 결과
None을 0으로 바꾸지 않고 실행 미확인으로 남깁니다.
code = None
print('unverified' if code is None else ('passed' if code == 0 else 'failed'))실행 결과
unverified
후보와 승인 연결
이전 후보 승인은 현재 후보 검토를 대신하지 않습니다.
revision = 'r2'
review = {'decision':'approve','revision':'r1'}
print('completed' if review['decision'] == 'approve' and review['revision'] == revision else 'review_pending')실행 결과
review_pending
로컬 관문 실제 실행
ZIP을 풀고 gate.py의 TODO를 수정한 폴더에서 실행합니다. 두 제공 검사 프로그램은 실제 프로세스로 실행되며 승인 기록을 전달하지 않으므로 마지막 상태는 review_pending입니다. 이어 bash check.sh로 실패·미확인·현재 및 과거 승인 경로를 확인합니다.
python3 gate.py실행 결과
unit_check.py:0 integration_check.py:0 review_pending
확인 문제
실습
gate.py의 종료 코드 TODO만 수정합니다. 단위 검사 실패 후 통합 검사는 미호출, 통합 실패는 repair, 실행 None은 unverified입니다. 두 검사 통과 후 현재 revision의 approve만 completed이고 과거 승인·미검토는 review_pending, 현재 reject는 repair입니다. bash check.sh로 9개 검사를 실행하고 python3 gate.py로 실제 두 프로그램의 종료 결과를 확인합니다. 테스트·검사 프로그램은 보존합니다.
실행 명령
bash check.sh
기대 결과
starter는 실패 종료 코드 관문 검사에 실패합니다. 수정 후 및 solution은 9개 검사 OK, 종료 코드 0입니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- AI가 만든 코드가 실행될 때 추가로 확인할 내용을 설명해 주시면 됩니다.