이 문서는 REST API 디버그 UI가 만든 fixture를 python-opinet-api의 회귀 테스트 자산으로 사용하는 방식입니다.
- Web UI는 테스트 코드를 생성하지 않고 fixture JSON을 생성합니다.
- pytest는 공통 runner로
tests/fixtures/{function}/{case}.json형식의 fixture를 자동 replay합니다. - 라이브러리 본체는 Streamlit에 의존하지 않습니다. Streamlit 앱은 별도 패키지에서
opinet.debug를 import해 사용합니다. - fixture 저장 전
certkey,api_key,Authorization, token 계열 값은<REDACTED>로 마스킹합니다. - replay 테스트는 외부 오피넷 API를 호출하지 않고 저장된
response.body만 사용합니다.
src/opinet/debug.py는 UI 없이도 사용할 수 있는 순수 Python 헬퍼를 제공합니다.
| 항목 | 역할 |
|---|---|
OpinetClient.debug() |
OpinetDebugClient 래퍼 반환 |
OpinetClient.debug_fetch(function_name, params) / OpinetDebugClient.debug_fetch(...) |
카탈로그 파라미터 메타데이터만으로 임의의 공식 API를 실행하는 제네릭 진입점. function_name별 분기 없이 getattr reflection으로 실제 공개 메서드를 호출한다. Streamlit UI가 쓰는 방식이다 |
DebugRun |
function, input, request, response, parsed, processed, trace, catalog item, error 보관 |
get_api_catalog() |
공식 5개 API 카탈로그 반환 |
get_api_catalog_options() |
Streamlit selectbox 등에 쓰기 좋은 label/value 목록 반환 |
jsonable() |
Pydantic v2, dataclass, enum, date/time 값을 JSON 저장 가능 값으로 변환 |
redact_sensitive() |
fixture 저장 전 민감정보 마스킹 |
save_fixture() / save_debug_fixture() |
표준 fixture JSON 저장 |
parse_debug_response() |
저장된 raw response를 기존 client parser로 재파싱 |
process_debug_result() |
parsed model을 normalized DTO로 변환 |
replay_fixture_case() |
fixture 한 건을 replay하고 assertion 수행 |
지원 function 이름은 현재 공식 5개 메서드와 같습니다.
from opinet import OpinetClient, ProductCode, get_api_catalog_options
from opinet.debug import save_debug_fixture
catalog_options = get_api_catalog_options()
# option["label"] 예: "반경 내 주유소 가격 (aroundAll.do)"
client = OpinetClient(api_key="...")
run = client.debug().search_stations_around(
lon=127.0276,
lat=37.4979,
radius_m=3000,
prodcd=ProductCode.GASOLINE,
)
save_debug_fixture(
base_dir="tests/fixtures",
debug_run=run,
case_name="gangnam gasoline normal",
description="강남역 반경 3km 휘발유 정상 검색 케이스",
)DebugRun은 선택된 API의 카탈로그 항목을 함께 가집니다. Streamlit Debug Trace 탭은 다음 값을 그대로 표시하면 됩니다.
st.markdown(f"**데이터셋:** {run.dataset_name}")
st.link_button("오피넷 인증키 발급", run.service_key_url)
st.json(run.trace_payload)run.trace_payload["catalog_item"]에는 dataset_name, endpoint, api_id, parameters, service_key_url이 포함됩니다.
기본 저장 위치는 tests/fixtures/{function}/{case}.json입니다. 기존 API 원본 fixture인 tests/fixtures/*.json와 충돌하지 않도록, replay runner는 하위 디렉터리 fixture만 읽습니다.
{
"name": "gangnam-gasoline-normal",
"function": "search_stations_around",
"description": "강남역 반경 3km 휘발유 정상 검색 케이스",
"catalog": {
"dataset_name": "반경 내 주유소 가격",
"endpoint": "aroundAll.do",
"service_key_url": "https://www.opinet.co.kr/user/custapi/openApiNew.do"
},
"input": {
"coordinate": {"lon": 127.0276, "lat": 37.4979},
"radius_m": 3000,
"prodcd": "B027",
"sort": "1"
},
"request": {
"method": "GET",
"url": "https://www.opinet.co.kr/api/aroundAll.do",
"query": {
"certkey": "<REDACTED>",
"out": "json",
"prodcd": "B027"
},
"headers": {}
},
"response": {
"status_code": 200,
"headers": {},
"body": {"RESULT": {"OIL": []}}
},
"parsed": [],
"processed": [],
"assertion": {
"mode": "snapshot",
"exclude_fields": ["fetched_at", "request_id", "updated_at"],
"required_fields": []
},
"meta": {
"created_at": "2026-05-15T00:00:00+09:00",
"library_version": "0.1.0",
"source": "debug_ui"
}
}parsed와 processed는 UI preview와 사람의 검토를 위한 저장값입니다. replay 테스트의 actual 값은 항상 response.body를 다시 파싱해 만듭니다.
| Mode | 동작 |
|---|---|
snapshot |
processed actual과 fixture의 processed를 비교합니다. exclude_fields는 key 또는 dotted path로 제외합니다. |
schema_only |
파싱과 processed 변환이 성공하고 actual이 None이 아니면 통과합니다. |
required_fields |
actual dict 또는 list 안의 모든 dict가 지정 필드를 갖는지 확인합니다. |
count |
actual list 길이 또는 dict의 count 값을 expected와 비교합니다. |
관련 파일은 다음과 같습니다.
| 파일 | 역할 |
|---|---|
tests/runners.py |
지원 function과 replay entrypoint |
tests/utils.py |
assertion helper re-export |
tests/test_generated_fixtures.py |
tests/fixtures/*/*.json 자동 발견 및 replay |
tests/fixtures/get_area_codes/area_code_root_schema_only.json |
runner smoke fixture |
실행:
python -m pytest tests/test_generated_fixtures.py전체 기본 테스트에서도 replay runner가 함께 실행됩니다.
Streamlit 프로젝트를 별도 패키지로 만들 때는 다음 경계를 지킵니다.
- UI 프로젝트에서만
streamlit,pandas같은 화면 의존성을 둡니다. - 개발 중에는
pip install -e ../python-opinet-api로 라이브러리를 import합니다. - 릴리즈 검증은 wheel 설치 후 UI 실행으로 확인합니다.
- UI의 Save 버튼은
save_debug_fixture(base_dir="tests/fixtures", debug_run=run, ...)만 호출합니다. - API 선택 UI는
get_api_catalog_options()의label을 보여주고value로 debug function을 선택합니다. - Debug Trace 탭은
run.trace_payload와run.service_key_url을 표시합니다. - Fixture/Testcase 탭은 case name, description, assertion mode, exclude fields, required fields, overwrite 설정을 받아
tests/fixtures/{function}/{case}.json으로 저장합니다. - Copy pytest code는 보조 기능으로만 둡니다. 기본 회귀 테스트는 parametrize runner 방식입니다.
이 저장소에는 바로 참고할 수 있는 선택형 예제 examples/streamlit_debug_ui.py가 있습니다. 예제는 Raw Response/Pydantic Model/Processed Result/Validation Errors/Debug Trace/Fixture 탭을 제공하지만, Streamlit은 라이브러리 런타임 의존성이 아니므로 debug-ui extra로 별도 설치해 실행합니다.
pip install -e ".[debug-ui]"
streamlit run examples/streamlit_debug_ui.py