Devin.KR

회귀 검사 범위 정하기

90분 안팎

학습 목표

완료 API를 추가하고 생성·조회 계약의 회귀 테스트를 함께 실행합니다.

개념

새 경로가 기존 약속을 바꿀 수 있습니다

완료 API를 연결하면서 오류 처리기를 고치면 생성 요청의 오류 응답까지 달라질 수 있습니다. 완료만 통과해도 제목의 타입 검사나 목록 순서가 손상될 수 있다는 뜻입니다. 이번 레슨은 앞 모듈의 생성·조회 검사를 보존하고 완료 상태 전이의 HTTP 검사를 추가합니다. 변경한 경로뿐 아니라 공유하는 서비스와 오류 변환 경계를 회귀 범위로 선택합니다. 교육 담당자는 검사 개수 증가보다 이전 약속과 새 약속이 함께 확인되는지 질문합니다.

이전 미션을 출발점으로 사용합니다

로컬 실습은 m04 미션 solution의 명세, Python 참조 모델, Java 서비스, 기존 검사와 웹 경계를 이어받습니다. 완료 서비스는 이미 있으므로 저장소를 재설계하지 않습니다. 승인된 새 파일은 CompletionHttpTest와 세 변형 검출 검사입니다. 앞 모듈의 보호 정책은 이번 명세·추가 검사 파일을 포함하도록 새 기준으로 구성되어 있습니다. 학습자는 이 기준을 임의로 고쳐 통과시키지 않습니다. 이번 기능 연결은 TaskController에서 이루어지고 입력 파싱과 응답 상태만 새로 결정합니다.

완료 HTTP 계약을 분리합니다

POST /tasks/{id}/complete는 요청 본문 없이 사용합니다. 성공은 200과 id·title·done을 가진 Task JSON입니다. 완료 재요청도 200이며 같은 객체 값을 돌려줍니다. 경로의 0·음수·abc·1.5는 400 INVALID_ID이고 없는 양의 정수는 404 NOT_FOUND입니다. 큰 양의 정수도 형식상 정수라면 없는 번호로 취급합니다. 서비스가 문자열 ID를 거절하던 계약은 유지하고 경로 문자열을 BigInteger로 읽어 서비스에 전달하는 HTTP 변환을 추가합니다.

정상과 오류 경로를 작은 수정으로 연결합니다

starter는 완료 성공을 201로 보내고 없는 번호 오류도 400으로 보냅니다. 이 두 응답을 완료 계약의 200·404로 고칩니다. 기존 생성의 201은 그대로 유지합니다. IllegalArgumentException 처리기에서 NOT_FOUND만 404로 나누고 다른 업무 오류는 400으로 보냅니다. NumberFormatException은 경로 파싱 위치에서 INVALID_ID로 바꿉니다. 오류 처리 전체를 삭제하거나 모든 예외를 404로 바꾸면 기존 제목 오류가 달라지므로 회귀 검사에서 걸러져야 합니다.

두 항목이 한 항목보다 잘 구별합니다

완료 검사에서 복습과 정리를 만든 뒤 번호 1만 완료합니다. 응답은 번호 1의 done=true이며 GET 결과는 번호 1 true, 번호 2 false 순서입니다. 한 항목만 준비하면 전체 항목을 완료하는 잘못된 구현도 통과할 수 있습니다. 두 항목을 쓰는 이유는 서로 영향이 없다는 불변 조건을 확인하기 위해서입니다. 생성 후 전체 JSON을 비교하여 제목과 번호가 초기 계약대로 준비되었는지도 판단합니다. 개수만 확인하는 검사로 바꾸지 않습니다.

오류 뒤 조회와 생성을 이어 봅니다

없는 번호 99를 완료하면 404를 확인하고 GET으로 두 항목이 모두 미완료인지 비교합니다. 이어서 새 항목을 생성하여 id=3인지 확인합니다. 이 순서는 오류 처리 중 저장 상태와 다음 번호가 손상되지 않았다는 증거입니다. 오류 메시지 하나만 비교하는 것보다 검사 비용은 조금 늘지만 앱 계약의 중요한 부작용을 관찰합니다. 오류 뒤 목록을 직접 비워 준비 상태로 되돌리면 결함이 가려지므로 같은 서비스 상태를 유지합니다.

회귀 목록을 영향 경로로 고릅니다

완료 컨트롤러는 TaskService와 오류 처리기를 공유합니다. 따라서 기존 제목 타입, 공백 정리, 길이 경계, 생성 실패 뒤 번호, 두 생성 뒤 조회 순서가 회귀 대상입니다. 서비스의 단위 검사와 HTTP의 MockMvc 검사를 함께 실행합니다. Python 참조 모델은 명세 비교를 위한 별도 실행 자료입니다. 이번 기능에 관계없는 운영 설정이나 전자책 화면을 검사하려고 작업 범위를 넓히지 않습니다. 영향이 닿는 계약은 포함하고 다른 시스템은 미확인으로 기록합니다.

선택 실행은 전체 관문을 대신하지 않습니다

개발 중에는 CompletionHttpTest만 골라 실행하여 빠르게 상태 코드 오류를 확인할 수 있습니다. 수정 후에는 ./mvnw test로 기존 서비스·HTTP·범위·변형 검출 전체를 실행합니다. 선택 실행 결과를 전체 통과라고 적지 않습니다. 이 과제는 작고 외부 DB가 없으므로 전체 실행 비용이 크지 않습니다. 보고서에서 이전 테스트가 사라지거나 건너뛰어진 것은 아닌지도 확인합니다. 완료가 추가되었다는 이유로 기존 생성의 기대 상태를 200으로 바꾸면 안 됩니다.

MockMvc의 관찰 범위를 설명합니다

이 실습은 standaloneSetup으로 명시한 컨트롤러와 서비스 객체를 연결합니다. JSON 변환, 경로 매핑, 상태 코드와 예외 처리의 MVC 계약을 확인합니다. 실제 소켓 서버를 띄우거나 전체 Spring 빈 구성을 검증하지 않습니다. 따라서 앱 기동·네트워크·운영 배포·동시 요청은 미확인입니다. 필요할 때는 별도 통합 검사를 계획할 수 있지만 여기서 수행하지 않은 결과를 통과라고 쓰지 않습니다. 같은 한계를 앞 모듈 기록과 일관되게 유지합니다.

실패 메시지에서 경계를 찾습니다

Status expected 200 but was 201이면 서비스 완료 자체보다 HTTP 성공 상태를 먼저 확인합니다. 404 대신 400이면 NOT_FOUND 변환 분기를 읽습니다. No value at JSON path는 예상 응답 대신 오류 객체나 빈 응답이 온 상황일 수 있으므로 실제 본문부터 확인합니다. 생성 numericTitle 검사가 갑자기 실패하면 공유 오류 처리기나 입력 변환을 검토합니다. 포트를 바꾸는 것으로 이런 MockMvc 계약 문제를 해결할 수 없으므로 응답을 만든 경계에 집중합니다.

테스트 수정에도 이유가 필요합니다

요구가 바뀌어 기존 기대값을 변경해야 할 때에는 변경 전후 명세와 영향을 받는 사례를 먼저 적습니다. 이번 완료 API 확장은 생성·조회 요구를 바꾸지 않으므로 기존 테스트는 보존합니다. 새 검사를 추가하는 것과 기존 기대를 완화하는 것은 다릅니다. 새 오류 코드가 생겼다는 이유로 모든 기존 오류 비교를 삭제하지 않습니다. review.md에는 채택한 구현 변경과 테스트 추가를 따로 기록하여 어떤 약속이 새로 생겼고 어떤 약속이 유지되었는지 보이게 합니다.

모듈 미션의 증거를 묶습니다

미션 starter에서도 같은 완료 응답 결함과 probe의 반례 누락을 수정합니다. spec.md의 완료 전이를 읽고 독립 기대값을 추가한 뒤 전체 Java 검사를 실행합니다. bash check_reference.sh로 이어받은 Python 모델도 재실행합니다. review.md에 세 변형 검출, 완료·재완료·오류 뒤 상태, 생성·조회 회귀의 결과를 구분하여 적습니다. 자동 범위 검사는 문서 내용의 타당성을 판단하지 않으므로 어떤 근거를 직접 판단했는지도 설명합니다.

완료 보고를 재현 가능하게 남깁니다

최종 보고에는 실행 위치, 명령, 종료 코드, 보고서의 검사 수와 실패·오류·건너뜀 수를 남깁니다. 예전 실행 결과를 현재 수정의 증거로 사용하지 않습니다. 새 완료 계약과 이전 계약이 모두 확인되었는지 표로 연결하고 아직 하지 않은 조건을 명시합니다. 다음 모듈의 오류 재현 작업자는 이 앱과 고정 사례를 그대로 이어받습니다. 통과했다는 한 줄보다 어떤 입력을 어떤 기대값과 비교했는지 알 수 있는 기록이 다음 세션의 판단을 돕습니다.

따라하기

영향 경로에서 회귀 범위를 고릅니다

완료 경로와 공유하는 서비스·응답 변환 계약을 검토합니다. 실제 API 검사가 아닌 회귀 계획의 출력입니다.

scope = {"서비스": ["제목", "번호", "완료"], "HTTP": ["생성 201", "조회 200", "완료 200", "없는 ID 404"]}
for layer, contracts in scope.items():
    print(layer + ": " + ", ".join(contracts))

실행 결과

서비스: 제목, 번호, 완료
HTTP: 생성 201, 조회 200, 완료 200, 없는 ID 404

다른 항목 보존 기대를 확인합니다

번호 1만 완료한 뒤의 기대 목록을 명세에서 직접 적습니다.

expected = [{"id":1,"title":"복습","done":True},{"id":2,"title":"정리","done":False}]
for task in expected:
    print(task["id"], task["title"], task["done"])

실행 결과

1 복습 True
2 정리 False

완료와 기존 회귀를 함께 실행합니다

solution에서 명령을 실행하고 surefire 보고서의 failures·errors·skipped가 모두 0인지 확인합니다. starter에서는 TaskController의 완료 응답 201과 없는 ID의 400을 각각 200·404로 고칩니다. 이어받은 Python 모델은 별도 명령으로 재실행합니다.

./mvnw test
bash check_reference.sh

실제 검사 수와 검출 관문을 읽습니다

solution에서 Maven 검사 뒤 실행합니다. tests는 assertion 수가 아니라 검사 사례 수입니다. 정상 통과와 세 오류 변형 검출을 나눠 확인합니다. starter를 수정한 뒤에도 동일 명령을 실행합니다.

python3 report.py

실행 결과

tests=46 failures=0 errors=0 skipped=0
boundaryDetected: PASS
missingDetected: PASS
normalPasses: PASS
repeatDetected: PASS

확인 문제

실습

TaskController.java의 완료 성공 상태와 NOT_FOUND 상태를 각각 200·404로 고칩니다. 기존 생성 201·조회 200·제목 오류 400을 보존합니다. CompletionHttpTest와 이전 검사 전체를 실행하고 review.md에 영향 경로·실행 결과·미확인을 적습니다.

시작 코드·테스트 내려받기

실행 명령

./mvnw test

기대 결과

tests=46 failures=0 errors=0 skipped=0 boundaryDetected: PASS missingDetected: PASS normalPasses: PASS repeatDetected: PASS; 종료 코드 0입니다.

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • AI가 만든 코드가 실행될 때 추가로 확인할 내용을 설명해 주시면 됩니다.