{"title":"خطوط اختبار ذاتية الإصلاح","slug":"self-healing-tests","category":"guides","summary":"ابنِ خطوط اختبار تتعلّم من الإخفاقات وتتكيف تلقائيًا باستخدام ذاكرة Synapse.","audience":["human","llm"],"tags":["guide","testing","self-healing","ci"],"difficulty":"advanced","updated":"2026-06-27","word_count":254,"read_minutes":1,"lang":"ar","translated":true,"requested_lang":"ar","content_markdown":"\n# خطوط اختبار ذاتية الإصلاح\n\nتنكسر مجموعات الاختبار التقليدية عند تغير الواجهة. تستخدم اختبارات\nالإصلاح الذاتي ذاكرة Synapse للتعلّم من الإخفاقات السابقة والتكيف — مما يقلل الاختبارات المتذبذبة\nوالعبء الصياني.\n\n## المفهوم\n\n```\n┌─────────┐  fails   ┌──────────┐  store   ┌──────────┐\n│  Test   │ ───────▶ │  Synapse │ ───────▶ │ Memories │\n│  Run    │          │  Memory  │          │ (failures)│\n└─────────┘          └──────────┘          └──────────┘\n                           ▲                     │\n                           │   recall            │\n                           │  before next run    │\n                           └─────────────────────┘\n```\n\n1. يُشغّل الاختبار\n2. إذا فشل، خزّن الفشل (ما الخطأ، لماذا، كيف الإصلاح)\n3. التشغيل التالي: استدعِ الإخفاقات ذات الصلة قبل التنفيذ\n4. طبّق الإصلاحات المعروفة تلقائيًا\n\n## التنفيذ\n\n### الخطوة 1: غلاف الاختبار\n\nغلّف كل اختبار باستدعاء ذاكرة/تخزين:\n\n```python\nimport requests\nfrom datetime import datetime\n\nURL = \"https://synapse.schaefer.zone\"\nMIND_KEY = \"mk_...\"\n\ndef self_healing_test(test_name, test_fn):\n    \"\"\"Decorator: wrap a test with self-healing memory.\"\"\"\n    def wrapper():\n        # 1. استدعِ الإخفاقات السابقة لهذا الاختبار\n        past_failures = requests.get(\n            f\"{URL}/memory/search?q={test_name}+failure\",\n            headers={\"Authorization\": f\"Bearer {MIND_KEY}\"}\n        ).json()\n        \n        # 2. شغّل الاختبار بسياق الإخفاق\n        try:\n            test_fn(known_failures=past_failures)\n        except Exception as e:\n            # 3. خزّن الفشل\n            store_failure(test_name, e, traceback.format_exc())\n            raise\n    \n    return wrapper\n\ndef store_failure(test_name, error, traceback_str):\n    requests.post(f\"{URL}/memory\",\n        headers={\"Authorization\": f\"Bearer {MIND_KEY}\",\n                 \"Content-Type\": \"application/json\"},\n        json={\n            \"category\": \"mistake\",\n            \"key\": f\"test_failure_{test_name}_{datetime.now().isoformat()}\",\n            \"content\": f\"Test: {test_name}\\nError: {error}\\nTrace:\\n{traceback_str}\",\n            \"tags\": [\"test\", \"failure\", test_name],\n            \"priority\": \"high\"\n        })\n```\n\n### الخطوة 2: منطق اختبار تكيّفي\n\nداخل الاختبار، تحقق من الإخفاقات المعروفة وطبّق الإصلاحات:\n\n```python\n@self_healing_test\ndef test_login_page(browser, known_failures=None):\n    browser.goto(\"https://app.com/login\")\n    \n    # تحقق إذا كنا رأينا تغير هذه الصفحة من قبل\n    if known_failures and known_failures.get(\"results\"):\n        for failure in known_failures[\"results\"]:\n            if \"button moved\" in failure[\"content\"].lower():\n                # استخدم تسمية الوصول بدلًا من الإحداثيات\n                browser.click(by_label=\"Login button\")\n                return\n    \n    # افتراضي: استخدم الإحداثيات\n    browser.click(x=150, y=400)\n```\n\n### الخطوة 3: استراتيجيات التعافي\n\nخزّن استراتيجيات التعافي كذكريات:\n\n```python\ndef store_recovery(failure_type, strategy):\n    requests.post(f\"{URL}/memory\",\n        headers={\"Authorization\": f\"Bearer {MIND_KEY}\",\n                 \"Content-Type\": \"application/json\"},\n        json={\n            \"category\": \"skill\",\n            \"key\": f\"recovery_{failure_type}\",\n            \"content\": strategy,\n            \"tags\": [\"test\", \"recovery\", failure_type],\n            \"priority\": \"high\"\n        })\n\n# خزّن التعافيات للإخفاقات الشائعة\nstore_recovery(\"element_not_found\",\n    \"When element not found by ID, try by CSS class, then by XPath, \"\n    \"then by accessibility label. Take screenshot for debugging.\")\n\nstore_recovery(\"timeout\",\n    \"Increase timeout to 30s. If still fails, check if page is loading \"\n    \"dynamically — wait for specific element instead of fixed time.\")\n\nstore_recovery(\"stale_element\",\n    \"Re-find element before each interaction. Don't cache element references \"\n    \"across page transitions.\")\n```\n\n### الخطوة 4: تكامل CI\n\n```yaml\n# .gitlab-ci.yml\ntest:self-healing:\n  script:\n    - export SYNAPSE_MIND_KEY=$SYNAPSE_TEST_MIND_KEY\n    - pytest tests/ --self-healing\n  after_script:\n    # لخّص الإخفاقات الجديدة\n    - python scripts/synapse_failure_summary.py\n```\n\n### الخطوة 5: لوحة تحليل الإخفاقات\n\n```python\n# احصل على جميع إخفاقات الاختبار من الأسبوع الماضي\nr = requests.get(\n    f\"{URL}/memory/search?q=test+failure\",\n    headers={\"Authorization\": f\"Bearer {MIND_KEY}\"}\n)\n\n# جمّع حسب اسم الاختبار\nfailures = {}\nfor mem in r.json().get(\"results\", []):\n    test_name = extract_test_name(mem[\"content\"])\n    failures.setdefault(test_name, []).append(mem)\n\n# التقرير\nfor test, fails in sorted(failures.items(), key=lambda x: -len(x[1])):\n    print(f\"{test}: {len(fails)} failures\")\n```\n\n## أفضل الممارسات\n\n> [!TIP]\n> - **خزّن التتبعات الخلفية (tracebacks)** — تحتوي السطر الدقيق الذي فشل\n> - **وسم حسب اسم الاختبار** — يُمكّن التصفية السريعة\n> - **استخدم فئة `mistake`** — يفصل عن الذكريات العادية\n> - **عيّن أولوية `high`** — يجب ألا تُنسى الإخفاقات أبدًا\n> - **تنظيف دوري** — احذف الذكريات للمشكلات المُصلَحة\n> - **لا تخزّن بيانات حساسة** — بيانات الاعتماد، PII\n\n## أنماط إخفاق شائعة للتخزين\n\n| نوع الإخفاق | ما يجب تخزينه |\n|--------------|---------------|\n| عنصر غير موجود | المُحدِّد المُجرَّب، حالة الصفحة، لقطة شاشة |\n| انتهاء المهلة | وقت الانتظار، ما كان يُنتظر |\n| فشل التأكيد | القيمة المتوقعة مقابل الفعلية |\n| خطأ شبكة | عنوان URL، رمز الحالة، نص الاستجابة |\n| رفض الإذن | الإذن المطلوب، دور المستخدم الحالي |\n\n## الخطوات التالية\n\n- [اختبار iOS المؤتمت](/docs/guides/automated-testing-ios)\n- [أفضل ممارسات الذاكرة](/docs/guides/memory-best-practices)\n- [كتاب طبخ التعافي من الأخطاء](/docs/llm-cookbook/error-recovery)\n","content_html":"<h1>خطوط اختبار ذاتية الإصلاح</h1>\n<p>تنكسر مجموعات الاختبار التقليدية عند تغير الواجهة. تستخدم اختبارات\nالإصلاح الذاتي ذاكرة Synapse للتعلّم من الإخفاقات السابقة والتكيف — مما يقلل الاختبارات المتذبذبة\nوالعبء الصياني.</p>\n<h2>المفهوم</h2>\n<pre><code class=\"hljs language-plaintext\">┌─────────┐  fails   ┌──────────┐  store   ┌──────────┐\n│  Test   │ ───────▶ │  Synapse │ ───────▶ │ Memories │\n│  Run    │          │  Memory  │          │ (failures)│\n└─────────┘          └──────────┘          └──────────┘\n                           ▲                     │\n                           │   recall            │\n                           │  before next run    │\n                           └─────────────────────┘</code></pre><ol>\n<li>يُشغّل الاختبار</li>\n<li>إذا فشل، خزّن الفشل (ما الخطأ، لماذا، كيف الإصلاح)</li>\n<li>التشغيل التالي: استدعِ الإخفاقات ذات الصلة قبل التنفيذ</li>\n<li>طبّق الإصلاحات المعروفة تلقائيًا</li>\n</ol>\n<h2>التنفيذ</h2>\n<h3>الخطوة 1: غلاف الاختبار</h3>\n<p>غلّف كل اختبار باستدعاء ذاكرة/تخزين:</p>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">import</span> requests\n<span class=\"hljs-keyword\">from</span> datetime <span class=\"hljs-keyword\">import</span> datetime\n\nURL = <span class=\"hljs-string\">&quot;https://synapse.schaefer.zone&quot;</span>\nMIND_KEY = <span class=\"hljs-string\">&quot;mk_...&quot;</span>\n\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">self_healing_test</span>(<span class=\"hljs-params\">test_name, test_fn</span>):\n    <span class=\"hljs-string\">&quot;&quot;&quot;Decorator: wrap a test with self-healing memory.&quot;&quot;&quot;</span>\n    <span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">wrapper</span>():\n        <span class=\"hljs-comment\"># 1. استدعِ الإخفاقات السابقة لهذا الاختبار</span>\n        past_failures = requests.get(\n            <span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{URL}</span>/memory/search?q=<span class=\"hljs-subst\">{test_name}</span>+failure&quot;</span>,\n            headers={<span class=\"hljs-string\">&quot;Authorization&quot;</span>: <span class=\"hljs-string\">f&quot;Bearer <span class=\"hljs-subst\">{MIND_KEY}</span>&quot;</span>}\n        ).json()\n        \n        <span class=\"hljs-comment\"># 2. شغّل الاختبار بسياق الإخفاق</span>\n        <span class=\"hljs-keyword\">try</span>:\n            test_fn(known_failures=past_failures)\n        <span class=\"hljs-keyword\">except</span> Exception <span class=\"hljs-keyword\">as</span> e:\n            <span class=\"hljs-comment\"># 3. خزّن الفشل</span>\n            store_failure(test_name, e, traceback.format_exc())\n            <span class=\"hljs-keyword\">raise</span>\n    \n    <span class=\"hljs-keyword\">return</span> wrapper\n\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">store_failure</span>(<span class=\"hljs-params\">test_name, error, traceback_str</span>):\n    requests.post(<span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{URL}</span>/memory&quot;</span>,\n        headers={<span class=\"hljs-string\">&quot;Authorization&quot;</span>: <span class=\"hljs-string\">f&quot;Bearer <span class=\"hljs-subst\">{MIND_KEY}</span>&quot;</span>,\n                 <span class=\"hljs-string\">&quot;Content-Type&quot;</span>: <span class=\"hljs-string\">&quot;application/json&quot;</span>},\n        json={\n            <span class=\"hljs-string\">&quot;category&quot;</span>: <span class=\"hljs-string\">&quot;mistake&quot;</span>,\n            <span class=\"hljs-string\">&quot;key&quot;</span>: <span class=\"hljs-string\">f&quot;test_failure_<span class=\"hljs-subst\">{test_name}</span>_<span class=\"hljs-subst\">{datetime.now().isoformat()}</span>&quot;</span>,\n            <span class=\"hljs-string\">&quot;content&quot;</span>: <span class=\"hljs-string\">f&quot;Test: <span class=\"hljs-subst\">{test_name}</span>\\nError: <span class=\"hljs-subst\">{error}</span>\\nTrace:\\n<span class=\"hljs-subst\">{traceback_str}</span>&quot;</span>,\n            <span class=\"hljs-string\">&quot;tags&quot;</span>: [<span class=\"hljs-string\">&quot;test&quot;</span>, <span class=\"hljs-string\">&quot;failure&quot;</span>, test_name],\n            <span class=\"hljs-string\">&quot;priority&quot;</span>: <span class=\"hljs-string\">&quot;high&quot;</span>\n        })</code></pre><h3>الخطوة 2: منطق اختبار تكيّفي</h3>\n<p>داخل الاختبار، تحقق من الإخفاقات المعروفة وطبّق الإصلاحات:</p>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-meta\">@self_healing_test</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">test_login_page</span>(<span class=\"hljs-params\">browser, known_failures=<span class=\"hljs-literal\">None</span></span>):\n    browser.goto(<span class=\"hljs-string\">&quot;https://app.com/login&quot;</span>)\n    \n    <span class=\"hljs-comment\"># تحقق إذا كنا رأينا تغير هذه الصفحة من قبل</span>\n    <span class=\"hljs-keyword\">if</span> known_failures <span class=\"hljs-keyword\">and</span> known_failures.get(<span class=\"hljs-string\">&quot;results&quot;</span>):\n        <span class=\"hljs-keyword\">for</span> failure <span class=\"hljs-keyword\">in</span> known_failures[<span class=\"hljs-string\">&quot;results&quot;</span>]:\n            <span class=\"hljs-keyword\">if</span> <span class=\"hljs-string\">&quot;button moved&quot;</span> <span class=\"hljs-keyword\">in</span> failure[<span class=\"hljs-string\">&quot;content&quot;</span>].lower():\n                <span class=\"hljs-comment\"># استخدم تسمية الوصول بدلًا من الإحداثيات</span>\n                browser.click(by_label=<span class=\"hljs-string\">&quot;Login button&quot;</span>)\n                <span class=\"hljs-keyword\">return</span>\n    \n    <span class=\"hljs-comment\"># افتراضي: استخدم الإحداثيات</span>\n    browser.click(x=<span class=\"hljs-number\">150</span>, y=<span class=\"hljs-number\">400</span>)</code></pre><h3>الخطوة 3: استراتيجيات التعافي</h3>\n<p>خزّن استراتيجيات التعافي كذكريات:</p>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">store_recovery</span>(<span class=\"hljs-params\">failure_type, strategy</span>):\n    requests.post(<span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{URL}</span>/memory&quot;</span>,\n        headers={<span class=\"hljs-string\">&quot;Authorization&quot;</span>: <span class=\"hljs-string\">f&quot;Bearer <span class=\"hljs-subst\">{MIND_KEY}</span>&quot;</span>,\n                 <span class=\"hljs-string\">&quot;Content-Type&quot;</span>: <span class=\"hljs-string\">&quot;application/json&quot;</span>},\n        json={\n            <span class=\"hljs-string\">&quot;category&quot;</span>: <span class=\"hljs-string\">&quot;skill&quot;</span>,\n            <span class=\"hljs-string\">&quot;key&quot;</span>: <span class=\"hljs-string\">f&quot;recovery_<span class=\"hljs-subst\">{failure_type}</span>&quot;</span>,\n            <span class=\"hljs-string\">&quot;content&quot;</span>: strategy,\n            <span class=\"hljs-string\">&quot;tags&quot;</span>: [<span class=\"hljs-string\">&quot;test&quot;</span>, <span class=\"hljs-string\">&quot;recovery&quot;</span>, failure_type],\n            <span class=\"hljs-string\">&quot;priority&quot;</span>: <span class=\"hljs-string\">&quot;high&quot;</span>\n        })\n\n<span class=\"hljs-comment\"># خزّن التعافيات للإخفاقات الشائعة</span>\nstore_recovery(<span class=\"hljs-string\">&quot;element_not_found&quot;</span>,\n    <span class=\"hljs-string\">&quot;When element not found by ID, try by CSS class, then by XPath, &quot;</span>\n    <span class=\"hljs-string\">&quot;then by accessibility label. Take screenshot for debugging.&quot;</span>)\n\nstore_recovery(<span class=\"hljs-string\">&quot;timeout&quot;</span>,\n    <span class=\"hljs-string\">&quot;Increase timeout to 30s. If still fails, check if page is loading &quot;</span>\n    <span class=\"hljs-string\">&quot;dynamically — wait for specific element instead of fixed time.&quot;</span>)\n\nstore_recovery(<span class=\"hljs-string\">&quot;stale_element&quot;</span>,\n    <span class=\"hljs-string\">&quot;Re-find element before each interaction. Don&#x27;t cache element references &quot;</span>\n    <span class=\"hljs-string\">&quot;across page transitions.&quot;</span>)</code></pre><h3>الخطوة 4: تكامل CI</h3>\n<pre><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># .gitlab-ci.yml</span>\n<span class=\"hljs-attr\">test:self-healing:</span>\n  <span class=\"hljs-attr\">script:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">export</span> <span class=\"hljs-string\">SYNAPSE_MIND_KEY=$SYNAPSE_TEST_MIND_KEY</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">pytest</span> <span class=\"hljs-string\">tests/</span> <span class=\"hljs-string\">--self-healing</span>\n  <span class=\"hljs-attr\">after_script:</span>\n    <span class=\"hljs-comment\"># لخّص الإخفاقات الجديدة</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">python</span> <span class=\"hljs-string\">scripts/synapse_failure_summary.py</span></code></pre><h3>الخطوة 5: لوحة تحليل الإخفاقات</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-comment\"># احصل على جميع إخفاقات الاختبار من الأسبوع الماضي</span>\nr = requests.get(\n    <span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{URL}</span>/memory/search?q=test+failure&quot;</span>,\n    headers={<span class=\"hljs-string\">&quot;Authorization&quot;</span>: <span class=\"hljs-string\">f&quot;Bearer <span class=\"hljs-subst\">{MIND_KEY}</span>&quot;</span>}\n)\n\n<span class=\"hljs-comment\"># جمّع حسب اسم الاختبار</span>\nfailures = {}\n<span class=\"hljs-keyword\">for</span> mem <span class=\"hljs-keyword\">in</span> r.json().get(<span class=\"hljs-string\">&quot;results&quot;</span>, []):\n    test_name = extract_test_name(mem[<span class=\"hljs-string\">&quot;content&quot;</span>])\n    failures.setdefault(test_name, []).append(mem)\n\n<span class=\"hljs-comment\"># التقرير</span>\n<span class=\"hljs-keyword\">for</span> test, fails <span class=\"hljs-keyword\">in</span> <span class=\"hljs-built_in\">sorted</span>(failures.items(), key=<span class=\"hljs-keyword\">lambda</span> x: -<span class=\"hljs-built_in\">len</span>(x[<span class=\"hljs-number\">1</span>])):\n    <span class=\"hljs-built_in\">print</span>(<span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{test}</span>: <span class=\"hljs-subst\">{<span class=\"hljs-built_in\">len</span>(fails)}</span> failures&quot;</span>)</code></pre><h2>أفضل الممارسات</h2>\n<div class=\"callout callout-ok\"></div><h2>أنماط إخفاق شائعة للتخزين</h2>\n<table>\n<thead>\n<tr>\n<th>نوع الإخفاق</th>\n<th>ما يجب تخزينه</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>عنصر غير موجود</td>\n<td>المُحدِّد المُجرَّب، حالة الصفحة، لقطة شاشة</td>\n</tr>\n<tr>\n<td>انتهاء المهلة</td>\n<td>وقت الانتظار، ما كان يُنتظر</td>\n</tr>\n<tr>\n<td>فشل التأكيد</td>\n<td>القيمة المتوقعة مقابل الفعلية</td>\n</tr>\n<tr>\n<td>خطأ شبكة</td>\n<td>عنوان URL، رمز الحالة، نص الاستجابة</td>\n</tr>\n<tr>\n<td>رفض الإذن</td>\n<td>الإذن المطلوب، دور المستخدم الحالي</td>\n</tr>\n</tbody></table>\n<h2>الخطوات التالية</h2>\n<ul>\n<li><a href=\"/docs/guides/automated-testing-ios\">اختبار iOS المؤتمت</a></li>\n<li><a href=\"/docs/guides/memory-best-practices\">أفضل ممارسات الذاكرة</a></li>\n<li><a href=\"/docs/llm-cookbook/error-recovery\">كتاب طبخ التعافي من الأخطاء</a></li>\n</ul>\n","urls":{"html":"/docs/guides/self-healing-tests","text":"/docs/guides/self-healing-tests?format=text","json":"/docs/guides/self-healing-tests?format=json","llm":"/docs/guides/self-healing-tests?format=llm"},"translations_available":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"]}