كلما اقتربت من معرفة Selenium WebDriver ، كلما كانت لدي أسئلة حول سبب تنفيذ هذه الوظيفة أو تلك بهذه الطريقة وليس بطريقة أخرى. يلقي أليكسي بارانتسيف في كلمته التي ألقاها
" مشكلات في Webenriver على
السيلينيوم" الضوء على التفاصيل الدقيقة لتطبيق أداة التشغيل الآلي هذه ويميز بين "الأخطاء" و "الميزات". ستجد الكثير من الأشياء المثيرة للاهتمام في الفيديو ، ولكن لا تزال هناك بعض النقاط (على الأقل بالنسبة لي) في الظل.
في هذه المقالة ، أريد مناقشة الأداة المستخدمة بشكل متكرر لانتظار حدث ما على إحدى الصفحات ، تم تنفيذها باستخدام فئة
WebDriverWait وطريقة عملها الرئيسية
حتى . أتساءل ما إذا كانت WebDriverWait مطلوبة على الإطلاق ، وهل يمكن رفضها؟
سيتم تقديم الأفكار في سياق C # ، على الرغم من أنني لا أعتقد أن منطق تنفيذ هذه الفئة سيكون مختلفًا عن لغات البرمجة الأخرى.
عند إنشاء مثيل WebDriverWait ، يتم تمرير مثيل برنامج التشغيل إلى المنشئ ، والذي يتم تخزينه في حقل
إدخال الإدخال . يفترض الأسلوب حتى المفوض الذي يجب أن يكون معلمة الإدخال IWebDriver الخاص به ، والذي يعتبر إدخالًا مثيلًا له.
لنلقِ نظرة على الكود المصدري للأسلوب
حتى . العمود الفقري لمنطقه هو دورة لا نهاية لها مع شرطين للخروج منه: بداية الحدث أو المهلة المطلوبة. تتجاهل "الأشياء الجيدة" الإضافية الاستثناءات المحددة مسبقًا وتعيد الكائن إذا لم يعمل البيز كـ TResult (المزيد حول ذلك لاحقًا).
القيد الأول الذي أراه هو أننا نحتاج دائمًا إلى مثيل لـ IWebDriver ، على الرغم من أنه داخل طريقة (حتى تكون دقيقة ، كمعلمة إدخال للشرط) ، يمكننا إدارة ISearchContext بالكامل. في الواقع ، في معظم الحالات ، نتوقع بعض العناصر أو التغيير في ممتلكاتها ونستخدم FindElement (s) للبحث عنه.
سأغامر بالقول إن استخدام ISearchContext سيكون أكثر منطقية ، لأن رمز العميل (الفئة) ليس مجرد كائن صفحة ، والذي يتم ، في البحث عن الأطفال ، صده من جذر الصفحة. في بعض الأحيان ، هذا هو الفصل الذي يصف عنصرًا مركبًا يكون جذره عنصرًا آخر في الصفحة ، وليس الصفحة نفسها. مثال على ذلك هو
SelectElement ، الذي يقبل إشارة إلى الأصل IWebElement في المُنشئ.
دعنا نعود إلى مسألة تهيئة WebDriverWait. يتطلب هذا الإجراء مثيل برنامج التشغيل. أي نحتاج دائمًا ، بطريقة أو بأخرى ، إلى رمي مثيل IWebDriver من خارج رمز العميل ، حتى لو كان فئة من بعض العناصر المركبة (مثال عن SelectElement) ، والذي يقبل بالفعل "أصل". من وجهة نظري ، هذا غير ضروري.
بالطبع ، يمكننا أن نعلن فئة عن طريق القياس
SearchContextWait : DefaultWait<ISearchContext>
ولكن دعونا لا نتسرع. نحن لسنا بحاجة إليه.
لنرى كيف يتم استخدام مثيل برنامج التشغيل الذي تم تمريره إلى الحالة. عادة ما يبدو مثل هذا:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.Until( d => d.FindElements(By.XPath("locator")).Count > 0 );
السؤال الذي يطرح نفسه ، لماذا يعد إصدار "محلي" من برنامج التشغيل ضروريًا إذا كانت الحالة متوفرة دائمًا من رمز العميل؟ علاوة على ذلك ، هذه هي نفس الحالة التي تم تمريرها مسبقًا من خلال المنشئ. أي قد يبدو الرمز بشيء من هذا القبيل:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.Until( d => Driver.FindElements(By.XPath("locator")).Count > 0 );
حتى سيمون ستيوارت يستخدم هذا النهج في
خطابه .

إنه لا يكتب "d -> d." ، لكنه يكتب "d -> driver." يتم ببساطة تجاهل مثيل برنامج التشغيل الذي تم تمريره إلى الطريقة. ولكن من الضروري أن يحيله ، لأن هذا مطلوب بواسطة توقيع الطريقة!
لماذا يمر السائق داخل حالة الأسلوب؟ من الممكن عزل البحث داخل هذه الطريقة ، كما هو مطبق في
ExpectedConditions ؟ إلقاء نظرة على تطبيق الأسلوب
TextToBePresentInElement . أو
VisibilityOfAllElementsLocatedBy . أو
TextToBePresentInElementValue . السائق المنقول لا يستخدم حتى في نفوسهم!
لذا ، فإن الفكرة الأولى هي أننا لسنا بحاجة إلى الأسلوب حتى مع المعلمة المفوض التي يقبلها السائق.
دعونا الآن معرفة ما إذا كان الأسلوب حتى يحتاج إلى قيمة الإرجاع؟ إذا كان التصرف المنطقي بمثابة TResult ، فلن يكون ذلك ضروريًا. في الواقع ،
في حالة النجاح ، سوف تتحقق ، وفي
حالة الفشل ، ستحصل على TimeoutException. ما هو محتوى المعلومات لمثل هذا السلوك؟
ولكن ماذا لو كان الكائن TResult؟ افترض البناء التالي:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); wait.IgnoreExceptionTypes(typeof(NoSuchElementException)); var element = wait.Until(d => d.FindElement(By.XPath("locator")));
أي لا ننتظر ظهور العنصر فحسب ، بل نستخدمه أيضًا (إذا انتظرنا) ، وبالتالي نزيل مكالمة غير ضرورية واحدة إلى DOM. جيد
دعونا نلقي نظرة فاحصة على هذه الأسطر الثلاثة من التعليمات البرمجية. داخل تطبيق الأسلوب حتى ، يتلخص في نوع من التشابه (رمز الشرطية)
try { FindElement } catch (NoSuchElementException) {}
أي سيتم طرح استثناء في كل مرة حتى يظهر العنصر في DOM. نظرًا لأن توليد الاستثناء يعد حدثًا مكلفًا إلى حد ما ، فإنني أفضل تجنبه ، خاصة في الأماكن التي يصعب فيها ذلك. يمكننا إعادة كتابة الكود كما يلي:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); var elements = wait.Until(d => d.FindElements(By.XPath("locator")));
أي نستخدم FindElements ، والتي لا تلقي استثناءً. انتظر ، هل سينتظر هذا التصميم ظهور العناصر؟ لا! لأنه ، إذا نظرت إلى
الكود المصدري ، تكتمل حلقة لا نهائية على الفور بمجرد إرجاع الشرط إلى غير فارغة. و FindElements في حالة الفشل تقوم بإرجاع مجموعة فارغة ، ولكن ليس فارغًا بأي شكل من الأشكال. أي للحصول على قائمة بالعناصر ، فإن استخدام حتى لا معنى له.
حسنا ، القائمة واضحة. ولكن لا يزال ، كيفية إرجاع العنصر الموجود وعدم رمي استثناء؟ قد يبدو الرمز كما يلي:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); var element = wait.Until(d => d.FindElements(By.XPath("locator")).FirstOrDefault());
في هذه الحالة ، في كل تكرار للحلقة ، لن نحصل فقط على قائمة IWebElement (التي قد تكون فارغة) ، ولكننا نحاول أيضًا استخراج العنصر الأول منه. إذا كانت العناصر لا تزال غير معروضة على الصفحة ، فسنحصل على قيمة خالية (القيمة الافتراضية للكائن) وننتقل إلى التكرار التالي للحلقة. إذا تم العثور على العنصر ، فسنخرج من الطريقة وسيتم تهيئة متغير العنصر بقيمة الإرجاع.
ومع ذلك ، فإن الفكرة الثانية هي أن قيمة الإرجاع للأسلوب حتى لا يتم استخدامها في معظم الحالات.
القيمة التي تم تمريرها غير ضرورية ، لا يتم استخدام قيمة الإرجاع. ما هي فائدة حتى؟ فقط في دورة وتيرة استدعاء طريقة الشرط؟ لقد تم بالفعل تنفيذ هذا النهج في C # في طريقة
SpinWait.SpinUntil . الفرق الوحيد هو أنه لا يلقي استثناء مهلة. يمكن إصلاح هذا كالتالي:
public void Wait(Func<bool> condition, TimeSpan timeout) { var waited = SpinWait.SpinUntil(condition, timeout); if (!waited) { throw new TimeoutException(); } }
أي تحل أسطر التعليمات البرمجية القليلة هذه في معظم الحالات محل منطق فئة WebDriverWait بأكملها. هل الجهود تستحق النتيجة؟
تحديثفي التعليقات على المقال ، أدلى مستخدم
المملكة العربية السعودية بملاحظة معقولة حول الفرق بين SpinUntil و حتى من حيث تواتر الحالة. بالنسبة إلى WebDriverWait ، هذه
القيمة قابلة للتعديل وتفترض أن 500 مللي ثانية. أي يحتوي الأسلوب حتى تأخير بين تكرار التكرار. بينما بالنسبة لـ SpinUntil ، يكون
المنطق معقدًا قليلاً ، وغالبًا ما لا تتجاوز فترة الانتظار 1 مللي ثانية.
في الممارسة العملية ، ينتج عن هذا الموقف ، أثناء انتظار عنصر يظهر خلال ثانيتين ، تقوم طريقة Unitl بإجراء 4 تكرارات ، وتستغرق طريقة SpinUntil حوالي 200 أو أكثر.
دعنا نتجاهل SpinUntil وأعد كتابة طريقة الانتظار كما يلي.
public void Wait(Func<bool> condition, TimeSpan timeout, int evaluatedInterval = 500) { Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed < timeout) { if (condition()) { return; } Thread.Sleep(evaluatedInterval); } throw new TimeoutException(); }
أضفنا بضعة سطور من التعليمات البرمجية ، وفي الوقت نفسه ، اقتربنا من منطق الأسلوب حتى.