Skip to main content

Pipeline di test self-healing

Costruisca pipeline di test che imparano dai fallimenti e si adattano automaticamente usando la memoria di Synapse.


Pipeline di test self-healing

Le suite di test tradizionali si rompono quando la UI cambia. I test self-healing usano la memoria di Synapse per imparare dai fallimenti passati e adattarsi — riducendo test flaky e carico di manutenzione.

Concetto

┌─────────┐  fails   ┌──────────┐  store   ┌──────────┐
│  Test   │ ───────▶ │  Synapse │ ───────▶ │ Memories │
│  Run    │          │  Memory  │          │ (failures)│
└─────────┘          └──────────┘          └──────────┘
                           ▲                     │
                           │   recall            │
                           │  before next run    │
                           └─────────────────────┘
  1. Il test viene eseguito
  2. Se fallisce, memorizza il fallimento (cosa è andato storto, perché, come riparare)
  3. Esecuzione successiva: richiama i fallimenti rilevanti prima di eseguire
  4. Applica le correzioni note automaticamente

Implementazione

Passo 1: wrapper del test

Avvolga ogni test con recall/store di memoria:

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

Passo 2: logica di test adattiva

All'interno del test, verifichi i fallimenti noti e applichi le correzioni:

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

Passo 3: strategie di recupero

Memorizzi le strategie di recupero come memorie:

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

Passo 4: integrazione 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

Passo 5: dashboard di analisi dei fallimenti

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

Best practice

Pattern di fallimento comuni da memorizzare

Tipo di fallimento Cosa memorizzare
Elemento non trovato Selettore provato, stato della pagina, schermata
Timeout Tempo di attesa, cosa si stava aspettando
Asserzione fallita Valore atteso vs attuale
Errore di rete URL, codice di stato, corpo della risposta
Permesso negato Permesso richiesto, ruolo utente corrente

Prossimi passi