Oyun Yazarı ve Yapay Zeka ile Kendi Kendini İyileştirme Testleri: Kesintiyi %70 Azaltın
E2E paketlerinin bakımında en sinir bozucu sorun, için kırılma testleri Küçük kullanıcı arayüzü değişiklikleri: kimliği değiştirin, düğmeyi taşıyın, CSS sınıfını yeniden adlandırın ve aniden günde 30 test başarısız oluyor. Değişikliği yapan geliştirici Gidip her testi manuel olarak düzeltmek zorunda kalıyoruz; bu, her biri saatlerce süren, tekrarlanan bir iş sprint ve genel olarak E2E testine karşı kızgınlığı besliyor.
I kendini iyileştirme testi bir seçicinin ne zaman olduğunu otomatik olarak algılamak için yapay zekayı kullanıyorlar artık çalışmıyor ve testi güncelleyerek mevcut kullanıcı arayüzündeki yeni doğru öğeyi bulun insan müdahalesi olmadan. Oyun yazarı, 2026'da bu yeteneği aracılığıyla entegre etti. akıllı konumlayıcılar ve Oyun Yazarı MCP ile entegrasyon.
Ne Öğreneceksiniz
- Testler neden bozulur ve en yaygın başarısızlık kalıpları
- Oyun Yazarı Bulucuları: yerel kırılganlık önleme stratejisi
- Playwright MCP ve Healenium ile kendi kendini iyileştiren kurulum
- Öncelik sırasına göre konum belirleme stratejileri: ARIA > test-id > CSS
- Kişiye özel şifa sisteminin uygulanması
- Metrikler: pul pulluk azalmasının nasıl ölçüleceği
E2E Testleri Neden Kırılıyor: Sebeplerin Analizi
İyileşmeyi uygulamadan önce, bozulmanın nedenlerini anlamalısınız. 10.000 analizden biri 2025 CI'sındaki test başarısızlığı şu dağılımı ortaya koyuyor:
Causa di rottura test E2E | Frequenza | Self-healing efficace?
-------------------------------- |-----------|----------------------
Selettore CSS/XPath cambiato | 38% | SI - alta probabilita
ID elemento rinominato | 22% | SI - cerca per testo/ARIA
Testo elemento cambiato | 15% | Parziale - dipende dal match
Timeout (lentezza server/UI) | 12% | NO - problema di infra
Logica di business cambiata | 8% | NO - richiede test update
Race condition | 5% | Parziale - retry aiuta
Arızaların %60-75'i "geçersiz seçici" kategorisine giriyor - tam olarak sorun kendi kendini iyileştirme sorunu çözer. Geriye kalan %25-40'lık kısım ise davranış için insan müdahalesini gerektirir Beklenen ve gerçekten değişti.
Oyun Yazarı Bulucuları: Kırılgan Olmayan Vakıf
Kendi kendini onaran yapay zeka hakkında konuşmadan önce, Oyun Yazarının bunu yerel olarak yerleşik hale getirdiğini anlamak önemlidir. Birçok kırılmayı önleyen güçlü yerelleştirme stratejileri:
// STRATEGIA PRIORITA LOCATOR — dal piu robusto al piu fragile
// 1. ARIA roles (raccomandato — semantica stabile)
await page.getByRole('button', { name: 'Accedi' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('test@example.com');
await page.getByRole('heading', { name: 'Dashboard' });
// 2. Text content (buono per elementi con testo stabile)
await page.getByText('Conferma prenotazione').click();
await page.getByLabel('Password').fill('secret');
await page.getByPlaceholder('Cerca...').fill('query');
// 3. Test ID (ottimo controllo, richiede attributo nel codice)
// Nel componente: <button data-testid="submit-btn">
await page.getByTestId('submit-btn').click();
// 4. CSS selector (evitare dove possibile)
// FRAGILE — si rompe con refactoring CSS
await page.locator('.btn-primary.submit').click(); // EVITARE
// 5. XPath (evitare — dipende dalla struttura DOM)
// MOLTO FRAGILE
await page.locator('//button[@class="btn submit"]').click(); // EVITARE
// Playwright auto-wait: non serve waitFor() nella maggior parte dei casi
// Playwright attende automaticamente che l'elemento sia visible, enabled, stable
await page.getByRole('button', { name: 'Salva' }).click();
// Equivalente a:
// await page.waitForSelector('button:text("Salva")', { state: 'visible' });
// await page.click('button:text("Salva")');
Oyun Yazarı ve Yapay Zeka ile Kendi Kendini İyileştirmeyi Uygulayın
Oyun Yazarı'nın 2026'da kendi kendini iyileştirmesine yönelik üç yaklaşım var:
Yaklaşım 1: Healenium (Açık Kaynak)
Healenium, Selenium ve Playwright ile entegre olarak sunulan açık kaynaklı bir kütüphanedir. orijinal seçici başarısız olduğunda alternatif seçiciler.
// playwright.config.ts con healing personalizzato
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: [
['html'],
['./reporters/healing-reporter.ts'] // reporter custom per tracciare healing events
],
use: {
actionTimeout: 10000,
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
// Self-healing locator wrapper personalizzato
// src/helpers/healing-locator.ts
import { Page, Locator } from '@playwright/test';
interface HealingResult {
original: string;
healed: string | null;
strategy: string;
confidence: number;
}
export class HealingPage {
private page: Page;
private healingLog: HealingResult[] = [];
constructor(page: Page) {
this.page = page;
}
/**
* Locator con fallback intelligente.
* Strategia: primary selector -> fallback selectors -> ARIA -> testo
*/
async smartLocator(
primarySelector: string,
fallbacks: string[] = [],
contextHint?: string
): Promise {
// Prova il selettore primario
const primaryLocator = this.page.locator(primarySelector);
const isPrimaryVisible = await primaryLocator.isVisible().catch(() => false);
if (isPrimaryVisible) {
return primaryLocator;
}
// Prova i fallback nell'ordine
for (const fallback of fallbacks) {
const fallbackLocator = this.page.locator(fallback);
const isFallbackVisible = await fallbackLocator.isVisible().catch(() => false);
if (isFallbackVisible) {
this.healingLog.push({
original: primarySelector,
healed: fallback,
strategy: 'explicit-fallback',
confidence: 0.9
});
console.log(`[HEALING] Healed: ${primarySelector} -> ${fallback}`);
return fallbackLocator;
}
}
// Fallback ARIA: cerca per ruolo e nome/hint
if (contextHint) {
const ariaLocator = this.page.getByRole('button', { name: contextHint })
.or(this.page.getByRole('link', { name: contextHint }))
.or(this.page.getByText(contextHint));
const isAriaVisible = await ariaLocator.isVisible().catch(() => false);
if (isAriaVisible) {
this.healingLog.push({
original: primarySelector,
healed: `aria:${contextHint}`,
strategy: 'aria-healing',
confidence: 0.75
});
return ariaLocator;
}
}
// Se nessun fallback funziona, lancia errore con contesto
throw new Error(
`[HEALING FAILED] Cannot locate element.\n` +
`Original: ${primarySelector}\n` +
`Tried fallbacks: ${fallbacks.join(', ')}\n` +
`Context hint: ${contextHint ?? 'none'}`
);
}
getHealingLog(): HealingResult[] {
return this.healingLog;
}
}
Yaklaşım 2: Yapay Zeka İyileştirmesi için Oyun Yazarı MCP
Oyun Yazarı MCP (Model Bağlam Protokolü), Yüksek Lisans'ların doğrudan etkileşime girmesine olanak tanır tarayıcı. Bu, gelişmiş olasılıkların önünü açar: Bir test başarısız olduğunda, bir yapay zeka temsilcisi analiz edebilir geçerli DOM'u seçin ve doğru seçiciyi önerin.
// Integrazione Playwright MCP per healing assistito da AI
// scripts/heal-failing-tests.ts
import { OpenAI } from 'openai';
import * as fs from 'fs';
const openai = new OpenAI();
interface FailedLocator {
selector: string;
lastKnownHTML: string;
currentPageHTML: string;
testFile: string;
lineNumber: number;
}
async function healSelector(failed: FailedLocator): Promise {
const prompt = `Sei un esperto di Playwright E2E testing.
Un test sta fallendo perche il selettore CSS non trova piu l'elemento.
Selettore originale (NON funzionante): ${failed.selector}
HTML dell'elemento quando il test funzionava:
${failed.lastKnownHTML}
HTML corrente della pagina (sezione rilevante):
${failed.currentPageHTML}
Analizza le differenze e suggerisci il nuovo selettore Playwright piu robusto.
Priorita: getByRole > getByTestId > getByText > locator con CSS specifico.
Rispondi con SOLO il nuovo selettore, senza spiegazioni.
Esempio di formato valido: page.getByRole('button', { name: 'Accedi' })`;
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: prompt }],
temperature: 0,
max_tokens: 150
});
return response.choices[0]?.message?.content?.trim() ?? null;
}
// Processa il report di failure di Playwright
async function processPlaywrightFailures(reportPath: string): Promise {
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
for (const suite of report.suites) {
for (const test of suite.tests) {
if (test.status === 'failed' && test.error?.message?.includes('locator')) {
console.log(`Processing failure in: ${test.title}`);
// Analizza e propone healing...
}
}
}
}
Pratik Uygulama: Kırılmaya Karşı Test Paketi
// tests/checkout.spec.ts — test scritto per resistere ai cambiamenti
import { test, expect } from '@playwright/test';
test.describe('Processo di checkout', () => {
test('utente completa un ordine con successo', async ({ page }) => {
await page.goto('/shop');
// USA getByRole — stabile anche se cambiano classi CSS
await page.getByRole('button', { name: 'Aggiungi al carrello' }).first().click();
// USA getByRole per navigazione — non dipende da URL
await page.getByRole('link', { name: 'Vai al carrello' }).click();
// Attendi conferma visuale prima di procedere
await expect(page.getByRole('heading', { name: 'Il tuo carrello' })).toBeVisible();
// Compila form con getByLabel — stabile rispetto a ristrutturazioni
await page.getByLabel('Nome completo').fill('Mario Rossi');
await page.getByLabel('Indirizzo email').fill('mario@example.com');
await page.getByLabel('Numero carta').fill('4242424242424242');
await page.getByLabel('Data scadenza').fill('12/28');
await page.getByLabel('CVV').fill('123');
// Usa data-testid per elementi critici senza testo ovvio
await page.getByTestId('place-order-btn').click();
// Assert su ARIA per conferma — non su testo esatto che puo cambiare
await expect(page.getByRole('alert', { name: /ordine confermato/i }))
.toBeVisible({ timeout: 10000 });
// Assert numero ordine con regex — robusto rispetto al formato
const orderNumber = page.getByText(/Ordine #\d+/);
await expect(orderNumber).toBeVisible();
});
});
Metrikler: İyileşme Nasıl Ölçülür?
// reporters/flakiness-reporter.ts — traccia la stabilita dei test
import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
import * as fs from 'fs';
class FlakinessReporter implements Reporter {
private results: Map<string, { passes: number; failures: number }> = new Map();
onTestEnd(test: TestCase, result: TestResult): void {
const key = test.titlePath().join(' > ');
const existing = this.results.get(key) ?? { passes: 0, failures: 0 };
if (result.status === 'passed') {
existing.passes++;
} else if (result.status === 'failed') {
existing.failures++;
}
this.results.set(key, existing);
}
onEnd(): void {
const report: Record<string, unknown> = {};
this.results.forEach((stats, testName) => {
const total = stats.passes + stats.failures;
const flakinessRate = total > 0 ? stats.failures / total : 0;
if (flakinessRate > 0) {
report[testName] = {
...stats,
total,
flakinessRate: (flakinessRate * 100).toFixed(2) + '%',
status: flakinessRate < 0.05 ? 'acceptable' : 'needs-attention'
};
}
});
fs.writeFileSync('flakiness-report.json', JSON.stringify(report, null, 2));
console.log(`\nFlakiness Report saved. Tests with issues: ${Object.keys(report).length}`);
}
}
export default FlakinessReporter;
E2E Suite için kararlılık hedefi
- Pullanma oranı < %0,5: üretimde kabul edilebilir
- %0,5 - %2: dikkat bölgesi, nedenleri analiz eder
- > %2: Derhal eylem gerekli — pakete olan güveni sarsıyor
- Kendi kendini iyileştirme özelliğine sahip hedef: Selektör arızalarında %60-70 azalma geçersiz; toplam pullanma %0,3'ün altında
Sonuçlar
Kendi kendini onaran testler, E2E testinin en sinir bozucu sorununun çözümüdür. Yol 2026'da en etkili olanı şu kombinasyondur: yerel Oyun Yazarı bulucularını kullanmak (getByRole, getByTestId) birincil önleme olarak kullanın ve kalan durumlar için bir AI iyileştirme katmanı ekleyin.
Yatırımın karşılığını hızlı bir şekilde amorti eder: Sprint başına 4 saatini bozuk testleri onarmaya harcayan bir ekip Kendini iyileştirmenin ilk ayında bu zamanı tamamen geri kazan.







