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

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

لن أتطرق إلى تثبيت البيئة الأساسية ، سأعطي بياناتي الأولية:
- سمور Ubuntu bionic
- PHP 7.1.19-1
- مؤلف - مدير إدارة التبعية لـ PHP ، مثبت عالميًا
- PhpStorm - بيئة التطوير
لإجراء الاختبارات ، نحتاج أيضًا إلى:
قم بتوسيع 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 ، والتي ستدفع نفسها الأوامر الضرورية من المكتبة:

نزيل الفائض ونكتب الطرق:
public function step_beingOnMainPage($page) { $this->amOnPage('/'); } public function step_seeElement($element) { this->seeElement($element); } public function step_clickOnButton($button) { $this->click($button); } 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:
public function step_keyboardButton() { $this->pressKey('//input',WebDriverKeys::ENTER); }
الخطوة الأخيرة هي عندما يتم وصف جميع الطرق الضرورية و PageObjects ، فأنت بحاجة إلى إعادة صياغة الاختبار نفسه. أضف الخطوات التي ستقوم بتهيئة PageObject عند الانتقال إلى صفحة جديدة. لدينا "* ذهب المستخدم إلى الصفحة: الصفحة".
للتوضيح ، سأضيف بضع خطوات أخرى. والنتيجة هي هذا الاختبار:
#language: ru : : . . . " " " " " " " " "" " " " " ENTER " - " " " " " " " "" ENTER
مثل هذا السيناريو الاختباري مفهوم ومقروء لأي شخص خارجي.
انطلق!
لعرض نتيجة تشغيل أكثر تفصيلاً ، يمكنك استخدام الأمر
cept run acceptance --debug
ننظر إلى النتيجة:

وبالتالي ، فإن استخدام نمط كائن الصفحة يسمح لك بفصل جميع عناصر الصفحة من البرامج النصية التجريبية وتخزينها في دليل منفصل.
يمكن العثور على المشروع نفسه على
https://github.com/Remneva/ProjectTutorialكمهندس أتمتة مبدئي ، سأكون ممتنًا إذا شاركت أفكارك ، وربما أخبرني بكيفية تحويل وتبسيط هيكل المشروع بشكل منطقي قدر الإمكان.