الانتظار والتزامن
التزامن (Synchronization) هو إبقاء الاختبار الآلي متوافقاً في توقيته مع التطبيق الذي يتحكم فيه: فلا تنفّذ أي خطوة إجراءها أو فحصها إلا بعد أن يبلغ التطبيق الحالة التي تحتاجها. مثال ذلك اختبار ينقر «Pay now» ويقرأ حالة الدفع فوراً، فيجد الصفحة ما زالت تعرض «Processing» لأن الخادم لم يردّ بعد، فيفشل الاختبار مع أن الدفع يعمل.
والانتظار (Wait) هو وسيلة الاختبار إلى التزامن. فالانتظار الجيد يسمّي شرطاً، مثل «الحالة تعرض Paid»، ويفحصه مرة بعد مرة، ويتابع بمجرد تحققه، في حدود مهلة قصوى (Timeout). يوفّر Selenium الانتظار الضمني والصريح، وينتظر Playwright وCypress تلقائياً قبل الإجراءات وفي التأكيدات. وهناك نوع واحد من الانتظار خاطئ دائماً: الانتظار الثابت (Fixed sleep).
لماذا يهم
التوقيت سبب شائع لتذبذب اختبارات الواجهة: اختبار ينجح على حاسوب سريع ويفشل على خادم CI مزدحم، أو يفشل مرة في كل عشرين تشغيلاً. وكل فشل زائف يكلّف أحداً وقتاً في التحقيق، والفريق الذي يرى كثيراً منه يبدأ بإعادة تشغيل البناءات الحمراء بدلاً من قراءتها. وهكذا يمرّ عيب حقيقي دون أن ينتبه إليه أحد.
والانتظار السيئ يكلّف وقتاً في كل تشغيل أيضاً، والحزمة البطيئة تُشغَّل مرات أقل. أما الانتظار الجيد فيجعل الحزمة أسرع وأجدر بالثقة في آن واحد: فهي تسير بسرعة التطبيق، وإذا فشلت ذكرت الرسالة الحالة التي لم تتحقق.
توقيت الاختبار وتوقيت التطبيق
يعمل الاختبار والتطبيق على ساعتين مختلفتين. فالأمر مثل النقر يعود بمجرد أن ينفّذه المتصفح، وينتقل الاختبار إلى سطره التالي. أما ما بدأه النقر فينتهي لاحقاً وفق جدول التطبيق: فكما رأيت في درس «أساسيات JavaScript للمختبِرين»، تعيد fetch() وعداً معلّقاً (Pending promise)، ولا تتغير الصفحة إلا عند وصول الاستجابة.
ولكي يتزامن الاختبار يحتاج إلى شرط يستطيع فحصه: شيء في الصفحة أو في عنوان URL أو في الشبكة لا يصبح صحيحاً إلا عندما يكون التطبيق جاهزاً للخطوة التالية. ومن التأخيرات المعتادة التي يجب التخطيط لها:
- طلبات الشبكة: التحميل والحفظ والبحث تنتهي عندما يردّ الخادم.
- العرض: قائمة أو إجمالي يظهر بعد وصول بياناته، وأحياناً على مرحلتين (عنصر نائب أولاً، ثم القيم).
- الحركات المرئية (Animations): نافذة حوار تنزلق إلى مكانها، أو زر يتحرك ريثما يستقر تخطيط الصفحة.
- المؤقتات: مربع بحث ينتظر 300 ملّي ثانية بعد آخر ضغطة مفتاح (Debounce)، أو رسالة تختفي بعد 5 ثوانٍ.
- التنقل: إعادة توجيه بعد تسجيل الدخول، أو تبويب جديد، أو تنزيل ملف.
لماذا لا يكون الانتظار الثابت حلاً أبداً
أسرع طريقة لجعل اختبار مستعجل ينجح هي إضافة توقف: Thread.sleep(3000) أو time.sleep(3) أو cy.wait(3000) أو page.waitForTimeout(3000). لكن الانتظار الثابت لا يعرف ما الذي ينتظره؛ فهو ينتظر مدة محددة ثم يتابع، سواء كان التطبيق جاهزاً أم لا. ويصف توثيق Playwright الاختبارات التي تنتظر الوقت بأنها متذبذبة بطبيعتها، وتعدّ أفضل ممارسات Cypress انتظار مدد اعتباطية نمطاً خاطئاً (Anti-pattern).
ويمكنك مشاهدة المشكلة مع نقطة نهاية بطيئة: فأداة «محاكي رموز حالة API» تردّ بعد تأخير تختاره، حتى 10 ثوانٍ. ويخطئ الانتظار الثابت بثلاث طرق:
- أقصر من اللازم في التشغيل البطيء: على خادم CI مزدحم تصل استجابة الدفع بعد 3.4 ثانية، فيقرأ الاختبار عند 3.0 ويفشل، لكن في بعض التشغيلات فقط، فيصبح الاختبار متذبذباً.
- أطول من اللازم في التشغيل السريع: إذا استغرقت الاستجابة 0.3 ثانية ينتظر الاختبار 3 ثوانٍ مع ذلك. ومئتا اختبار في كل منها انتظار كهذا تقضي مجتمعةً 10 دقائق في كل تشغيل دون أن تفعل شيئاً.
- بلا قصد واضح:
sleep(3000)لا تقول ما الذي كان الكاتب ينتظره، فلا القارئ التالي ولا رسالة الفشل يعرفان أيّ شرط كان مهماً.
انتظار ثابت:
waitForTimeout(3000)- الاختبار
- الصفحة التطبيق في المتصفح
- الخادم
- 1 من الاختبار إلى الصفحة: ينقر «Pay now» يعود الأمر بمجرد تنفيذ النقر
- 2 من الصفحة إلى الخادم: ترسل طلب الدفع
POST /api/payments - 3 الاختبار داخليًا: ينتظر 3 ثوانٍ ثابتة بلا شرط: لا ينظر إلى الصفحة أبداً
- 4 من الاختبار إلى الصفحة: يقرأ الحالة مرة واحدة عند 3.0 ثانية
- 5 من الصفحة إلى الاختبار: ما زالت «Processing» ردّ فشل
- 6 من الخادم إلى الصفحة:
201 Createdردّ عند 3.4 ثانية على خادم CI مزدحم، فتعرض الصفحة «Paid» بعد أن فشل الاختبار
النتيجة: يفشل مع أن الدفع نجح؛ وفي التشغيل السريع ينجح لكنه ينتظر 3 ثوانٍ كاملة. فشل
انتظار قائم على شرط: الحالة «Paid»
- الاختبار
- الصفحة التطبيق في المتصفح
- الخادم
- 1 من الاختبار إلى الصفحة: ينقر «Pay now» يعود الأمر بمجرد تنفيذ النقر
- 2 من الصفحة إلى الخادم: ترسل طلب الدفع
POST /api/payments - 3 من الاختبار إلى الصفحة: يفحص: هل الحالة «Paid»؟ مرة بعد مرة، حتى تصبح كذلك أو تنتهي المهلة
- 4 من الصفحة إلى الاختبار: ليس بعد: «Processing» ردّ
- 5 من الخادم إلى الصفحة:
201 Createdردّ عند 3.4 ثانية، فتعرض الصفحة «Paid» - 6 من الصفحة إلى الاختبار: «Paid»: ينتهي الانتظار عند 3.4 ثانية ردّ نجاح
النتيجة: يتابع بمجرد جاهزية الصفحة، عند 0.3 ثانية في التشغيل السريع؛ ولا يفشل إلا إذا لم تظهر «Paid» قبل انتهاء المهلة. نجاح
الانتظار الضمني والانتظار الصريح
لكل انتظار قائم على شرط ثلاثة أجزاء: الشرط نفسه، ومهلة تنهي الانتظار بخطأ، وفترة استطلاع (Polling interval) تحدد مدى تكرار فحص الشرط. ويوفّر Selenium WebDriver نوعين من الانتظار.
الانتظار الضمني (Implicit wait) إعداد واحد على مستوى الجلسة: كل بحث عن عنصر يعيد المحاولة حتى يصبح العنصر موجوداً في DOM أو تنتهي المهلة (والقيمة الافتراضية 0، أي الفشل فوراً). وهو ينتظر الوجود فقط، والعنصر قد يكون موجوداً لكنه مخفي أو معطّل أو مغطّى. كما يبطئ عمليات البحث التي يُفترض ألا تجد شيئاً: فالتحقق باستخدام findElements من غياب رسالة خطأ يكلّف المهلة الضمنية كاملة.
أما الانتظار الصريح (Explicit wait) فيُكتب عند الخطوة التي تحتاجه: كائن WebDriverWait بمهلة وشرط. وتوفّر ExpectedConditions (وexpected_conditions في Python) الشروط الشائعة، مثل: ظاهر، وقابل للنقر، وغير ظاهر، والنص موجود، وعنوان URL يحتوي على قيمة، ويمكنك تمرير دالتك الخاصة: d -> … في Java أو lambda d: … في Python، وهي الفكرة نفسها التي تقوم عليها الدالة السهمية (Arrow function) في JavaScript، وd هو المشغّل (Driver). ويفحص WebDriverWait الشرط افتراضياً كل 500 ملّي ثانية ويعود بمجرد تحققه. ويحذّر توثيق Selenium من الخلط بين الانتظار الضمني والصريح: فالمهلتان تتداخلان، وقد تفشل الخطوة بعد وقت أطول مما توحي به أيٌّ من القيمتين.
// A) Implicit wait: one session-wide setting, used by every findElement call.
// It waits until the element is present in the DOM, not visible or clickable.
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
// B) Explicit wait: a condition at the step that needs it. Use B instead of A,
// never both in the same session.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.cssSelector("[data-testid=payment-status]"), "Paid"));
wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("[data-testid=download-receipt]"))).click();
// Your own condition: a function the wait calls until it returns true
wait.until(d -> d.findElements(By.cssSelector("[data-testid=result-row]")).size() == 1);الانتظار التلقائي في Playwright وCypress
يُجري Playwright فحوص القابلية للتفاعل (Actionability) قبل كل إجراء. ففي click() يجب أن يكون العنصر ظاهراً، ومستقراً (لا يتحرك)، وقادراً على استقبال الأحداث (لا يغطيه عنصر آخر)، ومفعّلاً؛ وفي fill() يجب أن يكون ظاهراً ومفعّلاً وقابلاً للتحرير. وتتكرر الفحوص حتى تنجح أو تنتهي المهلة. والتأكيدات التي تعيد المحاولة، من درس التأكيدات، تنتظر الحالة المتوقعة بالطريقة نفسها. ويبحث المحدِّد (Locator) عن عنصره من جديد عند كل استخدام، فلا يترك عنصرٌ أُعيد عرضه الاختبارَ ممسكاً بمرجع قديم، وهو الاستثناء StaleElementReferenceException المعروف لمستخدمي Selenium.
ويعيد Cypress محاولة الاستعلامات مثل cy.get() وcy.contains() مع التأكيد المرتبط بها، مدة 4 ثوانٍ افتراضياً، وينتظر أن يصبح العنصر قابلاً للتفاعل قبل .click() أو .type(). ولانتظار الخادم أعطِ الطلب اسماً مستعاراً (Alias) باستخدام cy.intercept() وانتظر هذا الاسم، لا رقماً أبداً.
الانتظار التلقائي يتحقق من أن العنصر قابل للاستخدام، لا من أن التطبيق جاهز. فقد يكون زر «Pay now» مفعّلاً قبل تحميل إجمالي الطلب، وسينقره Playwright. وإذا كانت الخطوة تعتمد على بيانات فتحقّق من تلك البيانات أولاً. ولانتظار استجابة ابدأ الاستماع قبل الإجراء الذي يرسل الطلب، وإلا فقد تصل استجابة سريعة قبل أن يبدأ الانتظار. كما ينصح توثيق Playwright بعدم انتظار حالة networkidle في الاختبارات.
// Playwright: click() waits until the button is visible, stable, receives
// events and is enabled; toHaveText() retries until it matches (5 s default).
await page.getByRole('button', { name: 'Pay now' }).click();
await expect(page.getByTestId('payment-status')).toHaveText('Paid');
// Playwright: to wait for the server, start listening before the click.
const response = page.waitForResponse('**/api/payments');
await page.getByRole('button', { name: 'Pay now' }).click();
expect((await response).status()).toBe(201);
// Cypress: queries retry with their assertion (4 s default);
// an intercept alias waits for the request instead of a number.
cy.intercept('POST', '/api/payments').as('pay');
cy.contains('button', 'Pay now').click();
cy.wait('@pay');
cy.get('[data-testid=payment-status]').should('have.text', 'Paid');مثال تطبيقي: إصلاح اختبار دفع متذبذب
اختبار Playwright لصفحة الدفع يفشل في نحو تشغيل واحد من كل عشرة تشغيلات لخط الأنابيب (Pipeline) بالرسالة Expected: "Paid", Received: "Processing"، أما على حاسوب كاتبه فينجح دائماً. ويُظهر الأثر (Trace) أن قراءة الحالة حدثت بعد 3.0 ثانية من النقر وأن استجابة الدفع وصلت عند 3.4. كما تحمّل الصفحة إجمالي الطلب بعد تحميل الصفحة نفسها، وهذا ما كان الانتظار الثابت الأول يغطيه.
يستبدل الإصلاح كل انتظار ثابت بالشرط الذي كان يخمّنه. وقد قاس الفريق خطوة الدفع على بيئة Staging المشتركة: أقل من ثانية في العادة، ونحو 8 ثوانٍ في أسوأ الحالات. وهذا القياس هو ما يحدد المهلة الأطول الوحيدة. ولنقطة نهاية متاحة للعموم تعطيك أداة «محلّل زمن الاستجابة» بيانات من النوع نفسه: الوسيط والحد الأقصى وp95 (الزمن الذي بقيت ضمنه 95% من العيّنات) عبر عدة عيّنات.
- [1] يصبح الانتظار الثابت الأول تأكيداً على الإجمالي الظاهر، 172.50 ريالاً (172.50 SAR). فهو يقول ما المقصود بـ«تم التحميل»، ويكشف أيضاً أي إجمالي خاطئ.
- [2] و[3] يصبحان تأكيداً واحداً يعيد المحاولة. في التشغيل العادي يتابع خلال ثانية من النقر، ولا يُسمح بانتظار يصل إلى 15 ثانية إلا لهذه الخطوة، والتعليق يسجّل السبب.
- النقرة الأخيرة لا تحتاج إلى انتظار خاص بها: فـPlaywright ينتظر حتى يصبح زر «Download receipt» مفعّلاً.
- وإذا احتاج الدفع لاحقاً إلى أكثر من 15 ثانية، فأبلغ عن ذلك بوصفه مشكلة أداء مع التوقيتات، بدلاً من رفع المهلة مرة أخرى.
// BEFORE: always passes locally, fails in about one CI run in ten
await page.goto('/orders/1042/pay');
await page.waitForTimeout(2000); // [1] "let the total load"
await page.getByRole('button', { name: 'Pay now' }).click();
await page.waitForTimeout(3000); // [2] "let the payment finish"
const status = await page.getByTestId('payment-status').textContent();
expect(status).toBe('Paid'); // [3] reads once
await page.getByRole('button', { name: 'Download receipt' }).click();
// AFTER: each sleep replaced by the condition it was guessing at
await page.goto('/orders/1042/pay');
await expect(page.getByTestId('order-total')).toHaveText('172.50 SAR'); // [1]
await page.getByRole('button', { name: 'Pay now' }).click();
// [2] + [3]: payment sandbox measured at up to 8 s on shared staging
await expect(page.getByTestId('payment-status'))
.toHaveText('Paid', { timeout: 15_000 });
await page.getByRole('button', { name: 'Download receipt' }).click(); // auto-waitsأخطاء شائعة
- إصلاح فشل متذبذب بإضافة انتظار ثابت أو بإطالة انتظار موجود.
- ضبط انتظار ضمني واستخدام الانتظار الصريح أيضاً في جلسة Selenium نفسها.
- انتظار اختفاء مؤشر التحميل بدلاً من الحالة النهائية: فإذا لم يكن المؤشر قد ظهر بعد، نجح الانتظار فوراً.
- قراءة قيمة مرة واحدة بعد الإجراء مباشرة (
textContent()أو.text) بدلاً من تأكيد يعيد المحاولة أو انتظار صريح. - بدء انتظار استجابة الشبكة بعد النقرة التي ترسل الطلب؛ فتفوت الاستجابة السريعة وتنتهي مهلة الانتظار.
- رفع المهلة للحزمة كلها لأن خطوة واحدة بطيئة، مما يخفي انحدار الأداء (Performance regression) ويؤخّر الإبلاغ عن كل فشل حقيقي.
من منظور ضمان الجودة
في الفريق تصل مشكلات التزامن على شكل حالات فشل متذبذبة في خط الأنابيب. وعند فرز إحداها افتح الأثر أو الفيديو وقارن خطّين زمنيين: متى تصرّف الاختبار أو قرأ، ومتى تغيّر التطبيق. فإن كان الاختبار مستعجلاً فأصلح الانتظار. وإن كان التطبيق أبطأ من الحد المتفق عليه، أو بلغ حالة خاطئة، فذلك عيب يُبلَّغ عنه مع التوقيتات. فالإرسال المزدوج بعد نقرة ثانية متعجلة حالة تسابق (Race condition) في المنتج، لا ضجيج في الاختبار.
وفي مراجعة الشيفرة اطلب ألا يبقى أي انتظار ثابت في الاختبارات المحفوظة في المستودع، وأن تُستخدم المهل الافتراضية لإطار العمل، وألا تُطال المهلة إلا لخطوة واحدة مع تعليق يذكر السبب. واطلب من المطورين إشارات يمكن للاختبارات انتظارها: نص حالة، أو زر معطّل أثناء الحفظ، أو aria-busy="true" على منطقة أثناء تحميلها (سمة إتاحة تخبر قارئات الشاشة بأن المحتوى يتحدّث)، أو معرّف اختبار على النتيجة. فالإشارات الواضحة تفيد مستخدمي التقنيات المساعدة أيضاً.
تمرين عملي
أزل الانتظار الثابت من اختبار Selenium متذبذب
اختبار Python أدناه يسجّل الدخول ثم يبحث عن طلب ويتحقق من حالته. يفشل على CI نحو مرتين في الأسبوع، وفي سطر مختلف كل مرة. بعد تسجيل الدخول يعيد التطبيق التوجيه إلى صفحة الطلبات، التي تعرض أولاً كل طلبات العميل؛ والكتابة في مربع البحث تصفّيها بعد توقف مدته 300 ملّي ثانية، ومؤشر التحميل موجود في الصفحة دائماً لكنه مخفي، ويظهر أثناء تحميل النتائج المصفّاة.
اعثر على كل مشكلة تزامن، واشرح كيف يمكن أن تفشل كل منها، ثم أعد كتابة الاختبار باستخدام الانتظار الصريح فقط.
driver.implicitly_wait(10)
wait = WebDriverWait(driver, 10)
driver.get(BASE_URL + "/login")
driver.find_element(By.ID, "email").send_keys("layla.h@example.com")
driver.find_element(By.ID, "password").send_keys(PASSWORD)
driver.find_element(By.ID, "sign-in").click()
time.sleep(5)
driver.find_element(By.ID, "search").send_keys("ORD-1042")
wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR, ".spinner")))
time.sleep(1)
results = driver.find_elements(By.CSS_SELECTOR, "[data-testid=result-row]")
assert len(results) == 1
assert "Paid" in results[0].text- حدّد كل توقف ثابت، واكتب الشرط الذي ينتظره فعلاً.
- اعثر على الانتظار الذي قد ينجح قبل أن يبدأ الشيء الذي ينتظره أصلاً.
- حدّد كل سطر يقرأ الصفحة مرة واحدة، واذكر ما الذي قد يقرؤه مبكراً.
- افحص إعدادات المهلة بحثاً عن تركيبة يحذّر منها توثيق Selenium.
- أعد كتابة الاختبار بكائن
WebDriverWaitواحد وشرط واحد لكل خطوة.
إظهار الإجابة النموذجية
time.sleep(5) يخمّن المدة التي يستغرقها تسجيل الدخول وإعادة التوجيه. انتظر بدلاً منه إشارة الصفحة التالية نفسها: wait.until(EC.visibility_of_element_located((By.ID, "search")))، ثم اكتب في العنصر الذي يعيده.
انتظار مؤشر التحميل يعمل بعد الكتابة مباشرة، والمؤشر ما زال مخفياً لأن التوقف الذي مدته 300 ملّي ثانية لم ينتهِ بعد، فينجح invisibility_of_element_located فوراً ولا يثبت شيئاً. ثم يخمّن time.sleep(1) مدة التوقف وطلب البحث معاً.
سطر find_elements يقرأ القائمة مرة واحدة: ففي التشغيل البطيء تكون ما زالت تعرض الطلبات غير المصفّاة فيفشل len(results) == 1، كما يُقرأ results[0].text مرة واحدة أيضاً. استبدل انتظار المؤشر والتوقف والقراءتين بشرطين: wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, "[data-testid=result-row]")) == 1)، ثم wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, "[data-testid=result-row]"), "Paid")).
استخدام implicitly_wait(10) مع WebDriverWait خلطٌ بين الانتظار الضمني والصريح، ويحذّر توثيق Selenium من أنه يجعل أزمنة الانتظار غير متوقعة؛ كما يجعل كل استدعاء لـfind_elements لا يجد صفاً يتوقف حتى 10 ثوانٍ داخل الانتظار. احذفه وأنشئ wait = WebDriverWait(driver, 10) مرة واحدة. وتبقى أسطر تسجيل الدخول آمنة دونه إذا كان النموذج جزءاً من شيفرة HTML لصفحة الدخول، لأن driver.get() ينتظر تحميل الصفحة؛ أما إذا رسم سكربتٌ النموذجَ بعد التحميل فانتظر أولاً أن يظهر حقل البريد الإلكتروني.
الاختبار بعد إعادة كتابته بلا أي انتظار ثابت، وفيه ثلاثة شروط صريحة: مربع البحث ظاهر، وصف نتيجة واحد بالضبط، والصف يحتوي على «Paid». والتشغيل السريع يستغرق وقت التطبيق بدلاً من 6 ثوانٍ ثابتة، وعند الفشل تذكر الرسالة الشرط الذي لم يتحقق.
أسئلة شائعة
هل يكون الانتظار الضمني الخيار الصحيح أحياناً؟
قد ينفع في صفحات بسيطة تظهر عناصرها متأخرة لكنها تصبح قابلة للاستخدام بمجرد وجودها. لكن حدوده (الوجود فقط، وتطبيقه على كل بحث بما فيه البحث الذي يُفترض ألا يجد شيئاً، وسلوكه غير المتوقع مع الانتظار الصريح) تجعل كثيراً من الفرق تتركه على 0 وتكتفي بالانتظار الصريح.
مع الانتظار التلقائي في Playwright، هل ما زلت أكتب أوامر انتظار؟
نادراً كأسطر مستقلة. فالإجراءات تنتظر القابلية للتفاعل، والتأكيدات التي تعيد المحاولة تنتظر الحالات، لذا يكون معظم التزامن تأكيداً قبل الخطوة التالية. لكنك ما زلت تقرر ما الذي تنتظره، وتستخدم page.waitForResponse() أو page.waitForEvent() لانتظار استجابة أو تنزيل أو صفحة جديدة. ولا تحتاج أبداً إلى waitForTimeout() في اختبار محفوظ في المستودع.
كم يجب أن تكون المهلة؟
طويلة بما يكفي لأبطأ تشغيل سليم في أبطأ بيئاتك، استناداً إلى قياسات، ولا أطول. فالمهلة سقف لا تأخير: التشغيلات الناجحة لا تنتظرها كاملة أبداً. احتفظ بالقيم الافتراضية لإطار العمل (في Playwright Test 5 ثوانٍ لكل تأكيد و30 ثانية لكل اختبار، وفي Cypress 4 ثوانٍ لكل أمر)، وارفع المهلة للخطوة التي تعرف أنها بطيئة فقط.
كيف أختبر شيئاً يحدث بعد وقت طويل، مثل تحذير الخمول بعد 15 دقيقة؟
لا تنتظر 15 دقيقة. إذا كان المؤقت يعمل داخل الصفحة فتحكّم في الساعة: فـpage.clock.install() مع page.clock.fastForward() في Playwright، أو cy.clock() مع cy.tick() في Cypress، تقدّم مؤقتات الصفحة فوراً. وإذا كان الخادم هو من يفرض الوقت، فاطلب إعداد اختبار بحدّ أقصر.
المراجع
- Waiting Strategies — Seleniumلماذا تتسابق اختبارات المتصفح مع الصفحة، وكيف تزامن الانتظارات الضمنية والصريحة سكربت WebDriver مع التطبيق.
- Auto-waiting — Playwrightفحوص الجاهزية التي يجريها Playwright قبل كل إجراء — مرئي، ومستقر، ومفعّل، ويستقبل الأحداث — والتي تُغني عن معظم الانتظارات المكتوبة يدوياً.
- Best Practices — Cypressإرشادات Cypress لاختيار العناصر بسمات مخصّصة، وتجنّب الانتظار الثابت، وإبقاء الاختبارات مستقلة.
- WebDriver (W3C)بروتوكول W3C الذي تنفّذه المتصفحات لتتحكم فيها أدوات الأتمتة مثل Selenium عن بُعد (الإصدار Level 2 مسودة عمل).
تدرّب بأدوات متقن
احصل على نقطة نهاية عامة تعيد أي رمز حالة HTTP، مع تأخير اختياري وجسم JSON مخصّص، لاختبار معالجة الأخطاء في العميل.
قِس زمن استجابة نقطة نهاية عبر عدة عيّنات: الحد الأدنى والأقصى والمتوسط والوسيط وp95.
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.