Skip to main content

Pipeline ทดสอบที่ซ่อมแซมตัวเอง

สร้าง pipeline ทดสอบที่เรียนรู้จากความล้มเหลวและปรับตัวอัตโนมัติโดยใช้ Synapse memory


Pipeline ทดสอบที่ซ่อมแซมตัวเอง

test suite แบบดั้งเดิมจะพังเมื่อ UI เปลี่ยน การทดสอบที่ซ่อมแซมตัวเองใช้ Synapse memory เพื่อเรียนรู้จากความล้มเหลวในอดีตและปรับตัว — ลด flaky test และภาระการบำรุงรักษา

แนวคิด

┌─────────┐  fails   ┌──────────┐  store   ┌──────────┐
│  Test   │ ───────▶ │  Synapse │ ───────▶ │ Memories │
│  Run    │          │  Memory  │          │ (failures)│
└─────────┘          └──────────┘          └──────────┘
                           ▲                     │
                           │   recall            │
                           │  before next run    │
                           └─────────────────────┘
  1. ทดสอบรัน
  2. หากล้มเหลว เก็บความล้มเหลว (อะไรผิด, ทำไม, วิธีแก้)
  3. รันครั้งต่อไป: recall ความล้มเหลวที่เกี่ยวข้องก่อนปฏิบัติ
  4. ปรับใช้วิธีแก้ที่รู้โดยอัตโนมัติ

การ implement

ขั้นตอนที่ 1: Test Wrapper

ห่อหุ้มการทดสอบแต่ละครั้งด้วยการ recall/store memory:

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: Adaptive Test Logic

ภายในการทดสอบ ตรวจสอบความล้มเหลวที่รู้และปรับใช้วิธีแก้:

@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: กลยุทธ์การกู้คืน

เก็บกลยุทธ์การกู้คืนเป็น memory:

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 Integration

# .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.py

ขั้นตอนที่ 5: Failure Analysis Dashboard

# 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")

แนวทางปฏิบัติที่ดีที่สุด

รูปแบบความล้มเหลวทั่วไปที่ควรเก็บ

ประเภทความล้มเหลว สิ่งที่ควรเก็บ
Element not found selector ที่ลอง, สถานะหน้า, screenshot
Timeout เวลารอ, สิ่งที่กำลังรอ
Assertion failed ค่าที่คาดหวัง vs ค่าจริง
Network error URL, status code, response body
Permission denied permission ที่ต้องการ, role ผู้ใช้ปัจจุบัน

ขั้นตอนถัดไป