الأتمتة مع Codeception + Gherkin + PageObject للأصغر


نظرًا لعدم العثور على مثال ملموس واحد لتطبيق Gherkin مع نمط تصميم كائن الصفحة لميزة Codeception على الإنترنت ، اعتقدت أنه لن يكون من المناسب إخبار الإنترنت عن تنفيذنا لهذا النمط.

هذه المقالة مخصصة أكثر لأولئك الذين هم بالفعل على دراية بسيطة ببرنامج Codeception أو أطر العمل المماثلة ، ولكنهم لا يعرفون حتى الآن كيفية استخدام Test Object لجعل الاختبارات أكثر قابلية للقراءة ، وتبسيط دعمهم وتقليل كمية التعليمات البرمجية الإضافية. ومع ذلك ، حاولت أن أشرح خطوة بخطوة كل النقاط الرئيسية لتجميع مشروع الأتمتة من الصفر.

يسمح لك قالب كائن الصفحة بتغليف العمل بعناصر الصفحة ، والذي بدوره يسمح لك بتقليل كمية التعليمات البرمجية وتبسيط دعمها. يتم تنفيذ جميع التغييرات في واجهة المستخدم بسهولة وسرعة - ما عليك سوى تحديث فئة كائن الصفحة التي تصف هذه الصفحة. ميزة أخرى مهمة لهذا النهج المعماري هي أنه يسمح لك بعدم ازدحام نص اختبار HTML بالتفاصيل ، مما يجعله أكثر قابلية للفهم وأسهل للقراءة.

هكذا يبدو الاختبار دون استخدام كائن الصفحة



استخدام كائن الصفحة



لن أتطرق إلى تثبيت البيئة الأساسية ، سأعطي بياناتي الأولية:

  • سمور Ubuntu bionic
  • PHP 7.1.19-1
  • مؤلف - مدير إدارة التبعية لـ PHP ، مثبت عالميًا
  • PhpStorm - بيئة التطوير

لإجراء الاختبارات ، نحتاج أيضًا إلى:

  • Chromedriver 2.41 (لا يمكن تنزيل الرابط فقط ، ولكن أيضًا معرفة برنامج التشغيل الذي يتطابق مع إصدار Chrome)
  • كروم 67.0.3396.99
  • Selenium-server-standalone-3.14.0 (مقالة عن تثبيت السيلينيوم على أوبونتو)

قم بتوسيع Codeception


تابع تثبيت Codeception:

نفتح في المحطة الدليل الذي نحتاجه ، حيث سنجمع المشروع ، أو ننشئ دليلًا للمشروع ونذهب إليه:

mkdir ProjectTutorial cd ProjectTutorial 

تثبيت إطار Codeception وتبعياته:

 composer require codeception/codeception --dev 



سيبدو ملف تثبيت التبعية composer.json في المشروع كما يلي:

 { "require": { "php": ">=5.6.0 <8.0", "facebook/webdriver": ">=1.1.3 <2.0", "behat/gherkin": "^4.4.0", "codeception/phpunit-wrapper": "^6.0.9|^7.0.6" }, "require-dev": { "codeception/codeception": "^2.5", "codeception/base": "^2.5" } } 

توسيع المشروع:

 php vendor/bin/codecept bootstrap 



يمكن العثور على مزيد من المعلومات حول كيفية تثبيت المشروع في الوثائق الرسمية .

في هذه المرحلة ، يتم إنشاء ثلاث مجموعات من الاختبارات في مشروعنا. بشكل افتراضي ، يقسمها Codeception إلى القبول والوظيفة والوحدة. بالنسبة لهذه المجموعات ، ينشئ Codeception أيضًا ثلاثة ملفات yml. نشير فيها إلى جميع التكوينات اللازمة ، وربط الوحدات والخصائص لتشغيل اختباراتنا.

هذا الدرس مبني على مثال اختبار القبول ، لذلك سأعطي الإعدادات في Acceptance.suite.yml.

افتح مشروعنا في PHP Storm (أو بيئة تطوير أخرى مفضلة) وانتقل إلى Acceptance.suite.yml (بشكل افتراضي ، يقع في مجلد الاختبارات / accept.suite.yml).
نكتب الحد الأدنى من التبعيات الضرورية ونولي دائمًا الاهتمام بالتنسيق. الوحدات مفصولة بعلامة "-" ويجب أن تكون على نفس المستوى ، وإلا ستنثر الأخطاء عند بدء الاختبار.

اتضح:

 actor: AcceptanceTester modules: enabled: - WebDriver: url: 'http://yandex.ru/' //    ,        browser: 'chrome' - \Helper\Acceptance //          gherkin: contexts: default: - AcceptanceTester 

وبعض الأعمال التحضيرية:

إنشاء دليل منفصل في جذر المشروع (لدي lib).
في هذا الدليل ، قم بإنشاء الملف القابل للتنفيذ run.sh ، والذي سيقوم بتشغيل Selenium و Chrome Driver.

نضع Selenium و Chrome Driver هنا ، ونكتب أمر التشغيل في run.sh:

 java -jar -Dwebdriver.chrome.driver=chromedriver_241 selenium-server-standalone-3.14.0.jar 

كيف يبدو في المشروع:



نعود إلى وحدة التحكم ونغير حقوق الوصول:

 chmod +x ./run.sh 

(ملاحظة: يجب أن تتطابق أسماء برامج التشغيل الموجودة في الدليل تمامًا مع تلك المحددة في أمر البدء).

يمكنك بدء Selenium و Webdriver الآن حتى لا تعود إلى ذلك. للقيام بذلك ، افتح علامة تبويب طرفية جديدة ، انتقل إلى الدليل حيث يوجد ملف run.sh واكتب أمر التشغيل:

 ~/AutomationProjects/ProjectTutorial/lib$ ./run.sh 

تأكد من تشغيل الخادم:



نتركها في حالة تشغيل. تم الانتهاء من هذا العمل التحضيري.

كتابة نص اختبار


ننتقل إلى إنشاء ملف ميزة لحالتنا التجريبية. للقيام بذلك ، يتم توفير أمر خاص في Codeception ، قم بتشغيله في وحدة التحكم:

 cept g:feature acceptance check 

(لاحظ "تحقق" - اسم اختباري)

نرى ملف check.feature الجديد في مجلد القبول.



لسنا بحاجة إلى المحتوى الافتراضي ، بل نحذفه على الفور ونكتب اختبارنا.

لكي يتعرف المجمّع على الأبجدية السيريلية ، لا تنسَ بدء النص باستخدام #language: ru.
نحن نكتب نصًا قصيرًا باللغة الروسية. أذكركم أن كل جملة يجب أن تبدأ بالكلمات الرئيسية: "متى" ، "ثم" ، "و" ، الرمز "*" ، إلخ.

على سبيل المثال ، أخذت موقع Yandex على الويب ، يمكنك أن تأخذ أيًا منه.



لمعرفة الخطوات الموجودة في الاختبار ، نقوم بتشغيل البرنامج النصي الخاص بنا في النهاية الطرفية:

 cept dry-run acceptance check.feature 



يتم عرض خطوات البرنامج النصي في وحدة التحكم ، ولكن تنفيذها غير متوفر بعد.

ثم نقوم بتشغيل أمر سينشئ تلقائيًا نماذج لتطبيق أساليبنا:

 cept gherkin:snippets acceptance 



يتم استبدال جميع الأسماء من البرنامج النصي التي كانت بين علامتي اقتباس بمتغيرات: arg.
نقوم بنسخها من الوحدة الطرفية ولصقها في ملف AcceptanceTester.php ، حيث ستكمن طرق العمل مع عناصر الصفحة.



قم بإعادة تسمية الطرق للقراء ، مع عكس جوهرها (اختياري) ، وكتابة تنفيذها.



كل شيء بسيط ، ولكنه أبسط إذا كنت تعمل في بيئة تطوير ذكية ، مثل Storm ، والتي ستدفع نفسها الأوامر الضرورية من المكتبة:



نزيل الفائض ونكتب الطرق:

 /** * @When      */ public function step_beingOnMainPage($page) { $this->amOnPage('/'); } /** * @Then    :element */ public function step_seeElement($element) { this->seeElement($element); } /** * @Then    :button */ public function step_clickOnButton($button) { $this->click($button); } /** * @Then    :field  :text */ public function step_fillField($field, $text) { $this->fillField($field, $text); } 

دعنا نرى ما حدث. أطلقنا فريقًا سيوضح لنا الأساليب (الخطوة) التي لدينا الآن تنفيذها.

 cept gherkin:steps acceptance 



نجاح!

ولكن لا تزال الخطوات في ملف الميزة غير معترف بها كطرق.



لكي تفهم Storm ما يجب فعله بالخطوات ، سنفعل حيلة في تنفيذ واجهة السياق من مساحة الاسم Gherkin Context.

 namespace Behat\Behat\Context { interface Context {} } 

لف فئة AcceptanceTester الخاصة بنا في مساحة الاسم والوراثة من السياق

 implements \Behat\Behat\Context\Context 



الآن ترتبط جميع خطوات ملف الميزة بتنفيذها:



لكي يفهم Webdriver ما ينقر عليه ومكان البحث ، تحتاج إلى استبدال أسماء العناصر وعناوين الصفحات القابلة للقراءة بالمحددات وعناوين URL المقابلة ، والتي ستندرج في الأساليب كوسيطات.

ثم نحصل على اختبار للنموذج:



ويمكنك تشغيل:

 cept run acceptance 



مرت

تقريبا. إذا كانت عناصر الصفحة تستغرق وقتًا طويلاً للتحميل ، فيمكنك إضافة الانتظار إلى الطريقة المطلوبة:

 $this->waitForElementVisible($element); 

نعود إلى سيناريو الاختبار لدينا. نحن منزعجون من فقدان إمكانية قراءة الاختبار بالكامل نظرًا لأنه بدلاً من الأسماء الواضحة للعناصر والصفحات ، نرى عناصر HTML و url.

إذا أردنا إصلاح ذلك ، فقد حان الوقت للانتقال إلى تنفيذ نمط كائن الصفحة.

انتقل إلى كائن الصفحة


في دليل _support ، قم بإنشاء دليل الصفحة ، حيث سنضع صفحات الفصل الخاصة بنا:

 php vendor/bin/codecept generate:pageobject MainPage 

كائن الصفحة الأول هو الصفحة الرئيسية لـ Yandex ، فلنطلق عليه MainPage ، سنطلق على الفصل نفسه:



نعلن هنا الحقول والأساليب الثابتة بحيث يمكن استدعاؤها دون إنشاء كائن.

نظرًا لأنه في تكوينات Acceptance.suite.yml ، حددنا بالفعل عنوان URL لصفحة البدء: yandex.ru ، بالنسبة للصفحة الرئيسية ، سيكون كافياً لتحديد

 public static $URL = '/'; 

بعد ذلك تأتي مجموعة من عناصر الصفحة. نحن نصف مواقع لتحديد عدد من العناصر ونعطيهم أسماء واضحة وفريدة.

تحتاج الآن إلى إضافة طريقة getElement ، والتي ستعيد محدد الموقع باسم العنصر من الصفيف.

نتيجة لذلك ، لدينا:

 <?php //location: tests/_support/Page/MainPage.php namespace Page; /**   */ class MainPage { public static $URL = '/'; public static $elements = array( ' ' => "//*[@id='wd-wrapper-_afisha']", ' ' => "//*[@data-statlog='afisha.title.link']", ' ' => "//*[@class='weather__icon weather__icon_ovc']|//*[@class='weather__icon weather__icon_skc_d']", ); public static function getElement($name){ return self::$elements[$name]; } } 

أضف فئتين من الصفحات:

/ ** ملصق * /



/ ** ملصق - نتائج البحث * /



نعود إلى فئة AcceptanceTester.php ، حيث كتبنا أساليبنا.
دعنا ننشئ فيه صفيفًا من فئات PageObject ، حيث سنعين الأسماء للصفحات ونشير إلى أسماء فئتها في مساحة الاسم:

  private $pages = array( " " => "\Page\MainPage", "" => "\Page\AfishaPage", " -  " => "\Page\AfishaResult" ); 

يتم إضافة كل PageObject جديد بالمثل إلى هذا الصفيف.

بعد ذلك ، نحتاج إلى إنشاء حقل الصفحة الحالية ، والذي سيخزن الرابط إلى PageObject للصفحة الحالية:

 private $currentPage; 

الآن سنكتب طريقة ، عندما نطلبها ، يمكننا الحصول على الصفحة الحالية وتهيئة فئة PageObject التي نحتاجها.

من المنطقي اتخاذ خطوة كهذه باستخدام الطريقة "عندما ينتقل المستخدم إلى صفحة" اسم الصفحة ". بعد ذلك ، ستبدو أبسط طريقة لتهيئة فئة PageObject ، بدون عمليات تحقق ، كما يلي:

 /** * @When     :page */ public function step_beingOn($page) { //   pageObject $this->currentPage = $this->pages[$page]; } 

الآن نكتب طريقة getPageElement ، والتي ستسمح لنا بالحصول على العنصر ، أو بالأحرى ، محدد موقعه من الصفحة الحالية:

 private function getPageElement($elementName) { //         $curPage = $this->currentPage; return $curPage::getElement($elementName); } 



بالنسبة للطرق التي تم تنفيذها بالفعل ، من الضروري استبدال الوسائط التي تلقيناها مباشرة من نص الميزة في البداية بعناصر من PageObject ، وهي:

 $arg 

سيأخذ الشكل

 getPageElement($arg)) 

ثم تتخذ طرقنا الشكل التالي:

 /** * @When     :page */ public function step_beingOnMainPage($page) { //      pageObject $this->currentPage = $this->pages[$page]; $curPage = $this->currentPage; $this->amOnPage($curPage::$URL); } /** * @Then    :element */ public function step_seeElement($element) { $this->waitForElementVisible($this->getPageElement($element)); $this->seeElement($this->getPageElement($element)); } /** * @Then    :button */ public function step_clickOnButton($button) { $this->click($this->getPageElement($button)); } /** * @Then    :field  :text */ public function step_fillField($field, $text) { $this->fillField($this->getPageElement($field), $text); } /** * @Then      :field */ public function step_deleteText($field) { $this->clearField($this->getPageElement($field)); } 

تمت إضافة طريقة أخرى لعرض نتائج البحث بالضغط على Enter:

 /** * @Then    ENTER */ public function step_keyboardButton() { $this->pressKey('//input',WebDriverKeys::ENTER); } 

الخطوة الأخيرة هي عندما يتم وصف جميع الطرق الضرورية و PageObjects ، فأنت بحاجة إلى إعادة صياغة الاختبار نفسه. أضف الخطوات التي ستقوم بتهيئة PageObject عند الانتقال إلى صفحة جديدة. لدينا "* ذهب المستخدم إلى الصفحة: الصفحة".

للتوضيح ، سأضيف بضع خطوات أخرى. والنتيجة هي هذا الاختبار:

 #language: ru :     :   .    .   .      " "     " "     " "     " "      ""     " "  " "     ENTER      " -  "     "   "       " "     " "  ""     ENTER 

مثل هذا السيناريو الاختباري مفهوم ومقروء لأي شخص خارجي.

انطلق!

لعرض نتيجة تشغيل أكثر تفصيلاً ، يمكنك استخدام الأمر

 cept run acceptance --debug 

ننظر إلى النتيجة:



وبالتالي ، فإن استخدام نمط كائن الصفحة يسمح لك بفصل جميع عناصر الصفحة من البرامج النصية التجريبية وتخزينها في دليل منفصل.

يمكن العثور على المشروع نفسه على https://github.com/Remneva/ProjectTutorial

كمهندس أتمتة مبدئي ، سأكون ممتنًا إذا شاركت أفكارك ، وربما أخبرني بكيفية تحويل وتبسيط هيكل المشروع بشكل منطقي قدر الإمكان.

Source: https://habr.com/ru/post/ar427031/


All Articles