본문 바로가기
C.W.K.
Stream
Lesson 11 of 12 · published

AI 서비스 API 테스트

~10 min · api, testing, contract

Level 0견습생
0 XP0/101 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

래퍼 API는 계약을 묶어두고, 뒤에 있는 모델은 그렇지 않아

AI 서비스를 CI에서 테스트할 때는 두 가지 계층을 살펴봐야 해:

  • API 계층 — 요청과 응답의 형태, 상태 코드, 인증, 요청 제한을 다뤄. 전통적인 계약 테스트지.
  • 모델 계층 — 의미와 행동의 정확성을 확인해. 평가 스타일의 테스트라고 볼 수 있어.

첫 번째 계층은 하위 소비자가 의존하는 부분이라 반드시 안정적이어야 해. 두 번째 계층은 합의된 범위 안에서는 변동을 허용해.

API 계약 테스트

  • OpenAPI / Pydantic 스키마 — 모든 응답이 문서화된 형태에 맞는지 검증해.
  • 상태 코드 매트릭스 — 빈 입력은 400, 인증 없음은 401, 요청 제한 초과는 429, 정상 경로는 200으로 떨어지는지 확인해.
  • 스트리밍 프로토콜 — SSE나 WebSocket에서 이벤트 유형, 청크 구분자, 종료 이벤트를 단언해.
  • 하위 호환성 — 필드가 추가될 때 기존 클라이언트 동작은 유지되어야 해. 폐기 예정 기간 없이 필드를 제거하는 건 절대 금지야.

모델 목킹

API 계약 테스트에서는 밑에 있는 모델을 목으로 대체해. 실제 모델 호출은 느리고 비싸고 비결정적이거든. 스키마에 맞는 미리 준비된 응답을 반환하는 가짜 어댑터를 주입하자. 실제 모델 동작은 평가 계층(별도 작업)에서 처리해.

Code

FastAPI와 Pydantic으로 API 계약 테스트하기·python
# tests/test_api_contract.py
from fastapi.testclient import TestClient
from myapp.main import app, get_brain
from myapp.schemas import ChatResponse

class FakeBrain:
    async def stream(self, *a, **kw):
        yield {'type': 'text', 'text': 'hello'}
        yield {'type': 'done', 'usage': {'in': 5, 'out': 1}}

app.dependency_overrides[get_brain] = lambda: FakeBrain()
client = TestClient(app)

def test_chat_returns_documented_shape():
    r = client.post('/api/chat', json={'message': 'hi'})
    assert r.status_code == 200
    ChatResponse.model_validate(r.json())     # raises if shape drifts

def test_chat_rejects_empty_input():
    r = client.post('/api/chat', json={'message': ''})
    assert r.status_code == 400

External links

Exercise

AI 엔드포인트 하나에 계약 테스트 5개를 작성해봐: 정상 경로, 빈 입력, 너무 큰 입력, 인증 없음, 요청 제한 형태. 빠르고 결정적인 테스트를 위해 가짜 브레인을 사용해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.