자가 치유 테스트 파이프라인
Synapse 메모리를 사용하여 실패로부터 학습하고 자동으로 적응하는 테스트 파이프라인 구축.
자가 치유 테스트 파이프라인
전통적인 테스트 스위트는 UI가 변경되면 깨집니다. 자가 치유 테스트는 Synapse 메모리를 사용하여 과거 실패로부터 학습하고 적응합니다 — 불안정한 테스트와 유지 관리 부담을 줄입니다.
개념
┌─────────┐ fails ┌──────────┐ store ┌──────────┐
│ Test │ ───────▶ │ Synapse │ ───────▶ │ Memories │
│ Run │ │ Memory │ │ (failures)│
└─────────┘ └──────────┘ └──────────┘
▲ │
│ recall │
│ before next run │
└─────────────────────┘- 테스트 실행
- 실패하면, 실패 저장 (무엇이 잘못되었는지, 왜, 어떻게 수정하는지)
- 다음 실행: 실행 전 관련 실패 회상
- 알려진 수정 자동 적용
구현
1단계: 테스트 래퍼
각 테스트를 메모리 회상/저장으로 래핑:
import requests
from datetime import datetime
URL = "https://synapse.schaefer.zone"
MIND_KEY = "mk_..."
def self_healing_test(test_name, test_fn):
"""Decorator: wrap a test with self-healing memory."""
def wrapper():
# 1. Recall past failures for this test
past_failures = requests.get(
f"{URL}/memory/search?q={test_name}+failure",
headers={"Authorization": f"Bearer {MIND_KEY}"}
).json()
# 2. Run test with failure context
try:
test_fn(known_failures=past_failures)
except Exception as e:
# 3. Store the failure
store_failure(test_name, e, traceback.format_exc())
raise
return wrapper
def store_failure(test_name, error, traceback_str):
requests.post(f"{URL}/memory",
headers={"Authorization": f"Bearer {MIND_KEY}",
"Content-Type": "application/json"},
json={
"category": "mistake",
"key": f"test_failure_{test_name}_{datetime.now().isoformat()}",
"content": f"Test: {test_name}\nError: {error}\nTrace:\n{traceback_str}",
"tags": ["test", "failure", test_name],
"priority": "high"
})2단계: 적응형 테스트 로직
테스트 내에서 알려진 실패를 확인하고 수정 적용:
@self_healing_test
def test_login_page(browser, known_failures=None):
browser.goto("https://app.com/login")
# Check if we've seen this page change before
if known_failures and known_failures.get("results"):
for failure in known_failures["results"]:
if "button moved" in failure["content"].lower():
# Use accessibility label instead of coordinates
browser.click(by_label="Login button")
return
# Default: use coordinates
browser.click(x=150, y=400)3단계: 복구 전략
복구 전략을 메모리로 저장:
def store_recovery(failure_type, strategy):
requests.post(f"{URL}/memory",
headers={"Authorization": f"Bearer {MIND_KEY}",
"Content-Type": "application/json"},
json={
"category": "skill",
"key": f"recovery_{failure_type}",
"content": strategy,
"tags": ["test", "recovery", failure_type],
"priority": "high"
})
# Store recoveries for common failures
store_recovery("element_not_found",
"When element not found by ID, try by CSS class, then by XPath, "
"then by accessibility label. Take screenshot for debugging.")
store_recovery("timeout",
"Increase timeout to 30s. If still fails, check if page is loading "
"dynamically — wait for specific element instead of fixed time.")
store_recovery("stale_element",
"Re-find element before each interaction. Don't cache element references "
"across page transitions.")4단계: CI 통합
# .gitlab-ci.yml
test:self-healing:
script:
- export SYNAPSE_MIND_KEY=$SYNAPSE_TEST_MIND_KEY
- pytest tests/ --self-healing
after_script:
# Summarize new failures
- python scripts/synapse_failure_summary.py5단계: 실패 분석 대시보드
# Get all test failures from the last week
r = requests.get(
f"{URL}/memory/search?q=test+failure",
headers={"Authorization": f"Bearer {MIND_KEY}"}
)
# Group by test name
failures = {}
for mem in r.json().get("results", []):
test_name = extract_test_name(mem["content"])
failures.setdefault(test_name, []).append(mem)
# Report
for test, fails in sorted(failures.items(), key=lambda x: -len(x[1])):
print(f"{test}: {len(fails)} failures")모범 사례
저장할 일반적인 실패 패턴
| 실패 유형 | 저장할 내용 |
|---|---|
| 요소를 찾을 수 없음 | 시도한 셀렉터, 페이지 상태, 스크린샷 |
| 시간 초과 | 대기 시간, 무엇을 기다리고 있었는지 |
| 어설션 실패 | 예상 값 vs 실제 값 |
| 네트워크 오류 | URL, 상태 코드, 응답 본문 |
| 권한 거부 | 필요한 권한, 현재 사용자 역할 |