Skip to main content

Pipeline Pengujian Self-Healing

Bangun pipeline pengujian yang belajar dari kegagalan dan beradaptasi otomatis menggunakan memori Synapse.


Pipeline Pengujian Self-Healing

Suite pengujian tradisional rusak saat UI berubah. Pengujian self-healing menggunakan memori Synapse untuk belajar dari kegagalan masa lalu dan beradaptasi — mengurangi pengujian flaky dan beban pemeliharaan.

Konsep

┌─────────┐  fails   ┌──────────┐  store   ┌──────────┐
│  Test   │ ───────▶ │  Synapse │ ───────▶ │ Memories │
│  Run    │          │  Memory  │          │ (failures)│
└─────────┘          └──────────┘          └──────────┘
                           ▲                     │
                           │   recall            │
                           │  before next run    │
                           └─────────────────────┘
  1. Pengujian berjalan
  2. Jika gagal, simpan kegagalan (apa yang salah, mengapa, cara memperbaiki)
  3. Berikutnya: recall kegagalan yang relevan sebelum mengeksekusi
  4. Terapkan perbaikan yang diketahui secara otomatis

Implementasi

Langkah 1: Pembungkus Pengujian

Bungkus setiap pengujian dengan recall/simpan memori:

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

Langkah 2: Logika Pengujian Adaptif

Di dalam pengujian, periksa kegagalan yang diketahui dan terapkan perbaikan:

@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)

Langkah 3: Strategi Pemulihan

Simpan strategi pemulihan sebagai memori:

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

Langkah 4: Integrasi 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.py

Langkah 5: Dashboard Analisis Kegagalan

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

Praktik Terbaik

Pola Kegagalan Umum untuk Disimpan

Tipe Kegagalan Apa yang Disimpan
Element not found Selector yang dicoba, kondisi halaman, tangkapan layar
Timeout Waktu tunggu, apa yang ditunggu
Assertion failed Nilai yang diharapkan vs aktual
Network error URL, kode status, body respons
Permission denied Izin yang diperlukan, peran pengguna saat ini

Langkah Berikutnya