LabHub
学习 学习路径 课程

테스트 도구 실전 · 예외를 주입해 자원 누수를 찾는다 · 实验

예외를 주입해 자원 누수를 찾는다

在 LabHub 中继续学习

목표

정상·중복 종료·실패 경로와 앱 lifespan의 시작·정리를 관측합니다.

왜 중요한가

요청이 성공하면 연결을 닫는 코드는 있었지만 예외가 발생하면 열린 연결이 남았다. 성공 응답만 확인하는 테스트에서는 이 누수가 드러나지 않았다. 수명 주기 테스트는 반환값보다 자원이 언제 열리고 닫혔는지에 집중해야 한다.

단계

1. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: new_resource()는 {open:False, events:[]}인 새 딕셔너리이며 호출끼리 events를 공유하지 않습니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.

mkdir -p /root/work/test-lifespan-cleanup-labtest -e /root/work/test-lifespan-cleanup-lab/service.py || cp /opt/fixtures/ten_labs/test-lifespan-cleanup-lab/service.py /root/work/test-lifespan-cleanup-lab/service.pytest -e /root/work/test-lifespan-cleanup-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lifespan-cleanup-lab/test_service.py /root/work/test-lifespan-cleanup-lab/test_service.pycd /root/work/test-lifespan-cleanup-lab

2. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: start(resource)는 이미 열려 있으면 ValueError, 아니면 open=True로 바꾸고 events에 'open'을 추가합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

3. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: stop(resource)는 열려 있을 때만 open=False로 바꾸고 'close'를 events에 추가합니다. 이미 닫혀 있으면 그대로 둡니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

4. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: read(resource)는 닫혀 있으면 RuntimeError, 열려 있으면 {ready:True}를 반환합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

5. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: scope(resource)는 contextmanager입니다. 진입 시 start, 블록 안에는 resource를 yield하고 블록의 성공·실패 모두 stop으로 닫습니다. 블록 예외는 전파합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

6. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: lifespan_for(resource)는 asynccontextmanager 함수 lifespan(app)을 반환합니다. scope(resource) 안에서 app.state.resource를 설정하고 yield합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

7. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: create_app(resource)는 lifespan_for를 사용합니다. GET /ready는 app.state.resource를 read한 결과를 반환합니다. context 종료 시 자원을 닫아야 합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

8. /root/work/test-lifespan-cleanup-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: exercise(resource, fail=False)는 with TestClient(create_app(resource)) 안에서 GET /ready를 호출합니다. fail=True면 그 안에서 RuntimeError를 내고, 아니면 응답 JSON을 반환합니다. 두 경우 모두 자원이 닫혀야 합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.

참고

8个步骤

  1. 자원 상태를 독립적으로 만든다 — 테스트
  2. 중복 시작을 거절한다 — 테스트
  3. 종료를 멱등하게 만든다 — 테스트
  4. 닫힌 자원의 사용을 막는다 — 테스트
  5. 예외 경로에 finally를 둔다 — 테스트
  6. 앱 수명주기와 자원을 연결한다 — 테스트
  7. 준비 상태를 실제 요청으로 읽는다 — 테스트
  8. 요청 이후의 실패도 정리한다 — 테스트