مستودع لورشة العمل الخاصة بي في WordCamp Catania 2019
اختياري، ولكن قد تحتاج إلى تثبيت عامل الإرساء:
sudo apt-get install docker-ce docker-ce-cli containerd.io
أفترض أنك قمت بتثبيت Composer. لنقم بتثبيت PHPUnit أولاً:
composer require --dev phpunit/phpunit ^8.3
من فضلك، تحقق أيضا من المتطلبات!
يتطلب PHPUnit 8.3 PHP 7.2 على الأقل! بالمناسبة - سينتهي الدعم الأمني لـ PHP 7.1 في الأول من ديسمبر 2019.
تلميح : ليس لديك برنامج Composer مثبتًا؟ جرب هذا!
docker run --rm -it -v $PWD:/app -u $(id -u):$(id -g) composer install
يوجد على الأقل إطاران صالحان يكونان في متناول يديك عندما تخطط لاختبار ملحقات WordPress:
دعونا نجرب قرد الدماغ :
composer require --dev brain/monkey:2.*`
سيؤدي هذا تلقائيًا إلى تثبيت Mockery وPatwork. ما عليك سوى تنفيذ composer install
وستكون جاهزًا للبدء.
قم بإنشاء دليل يوفر موطنًا لفئة اختبار صغيرة تسمى WcctaTest.php :
mkdir -p tests/wccta
ممتاز! لنقم الآن بإنشاء ملف تكوين phpunit.xml في الدليل الجذر.
يمكنك أيضًا أن تقرر تشغيل اختباراتك باستخدام معلمات التكوين من سطر الأوامر. شاهد الجزء التالي (تلميح: "البرامج النصية")!
عظيم! أضف بعض الأقسام إلى ملف Composer.json :
composer test
لنقم بإنشاء دليل يوفر موطنًا لرمز المصدر الخاص بنا. هذا هو المكان الذي ستضع فيه الفصل الأول الذي ستختبره قريبًا.
mkdir -p src/wccta && touch src/wccta/Plugin.php
rm -f tests/wccta/WcctaTest.php && touch tests/wccta/PluginTest.php
touch wordpress-plugins-phpunit.php
نريد اختبار بعض أساليب فئة Plugin
. تخيل طريقة تسمى is_loaded
والتي تعود true
عند النجاح. عندما تكون جاهزًا، قم بتنفيذ:
composer test
تلميح : نظامك أو إصدار PHP غير محدث؟ يمكنك فقط تخطي هذه الخطوة ولكن دعنا نجرب شيئًا جديدًا [ليس كذلك]!
docker run -it --rm -v $PWD:/app -w /app php:7.3-alpine php ./vendor/bin/phpunit
ربما يمكنك أن تتخيل أن بعض المكونات الإضافية ستحتوي على الكثير من الفئات، وأنه يمكنك بسهولة أن تنسى اختبار جميع الوظائف التي تحتاج إلى اختبار.
لذلك، دعونا نتحدث عن التغطية !
ما عليك سوى إضافة أمر مخصص إلى قسم البرامج النصية في ملف Composer.json الخاص بك:
"coverage": "./vendor/bin/phpunit --coverage-html ./reports/php/coverage"
ومرشح إلى phpunit.xml الخاص بك :
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src</directory>
</whitelist>
</filter>
الآن فقط قم بتنفيذ composer coverage
! سيؤدي هذا إلى إنشاء دليل ./reports/php/coverage
مع بعض ملفات html. حسنا، ليس على كافة أجهزة الكمبيوتر. سيظل البعض يتلقى رسائل خطأ مثل:
Error: No code coverage driver is available
دعونا نصلح ذلك في صورة عامل الإرساء لدينا. لقد قمت بإعداد ملف Dockerfile بحيث يمكنك تنفيذ ما يلي:
docker build -t coverage .
وبعد الانتهاء من عملية البناء:
docker run -it --rm -v $PWD:/app -w /app coverage:latest php ./vendor/bin/phpunit --coverage-html ./reports/php/coverage
الآن أنت تعرف الكونغ فو! من فضلك، افتح الملف ./reports/php/coverage/index.html في متصفحك!
دعونا نربط فئة Plugin
الخاصة بنا بالمكون الإضافي. قبل أن نبدأ فعليًا في الاختبار، سأوضح لك كيفية إعلان أجزاء من أكوادك غير قابلة للاختبار.
@codeCoverageIgnore
وهذا أحد الشروحات الهامة المتوفرة. سنعود إلى هذا لاحقًا، ولكن أولاً:
قم بإجراء اختبارات الوحدات باستخدام تقرير التغطية مرة أخرى!
ربما لاحظت عمود CRAP
في تقرير التغطية. CRAP هو اختصار للأنماط المضادة لمخاطر التغيير . إنه يشير إلى مدى خطورة تغيير التعليمات البرمجية في الفصل أو الطريقة. يمكنك تقليل المخاطر (وبالتالي الفهرس) باستخدام تعليمات برمجية أقل تعقيدًا وتغطية كاملة بالاختبارات.
لنبدأ باختبار شيء ما. ولكن ماذا؟ لا يوجد حتى الآن أي وظيفة أخرى مكتوبة تحتاج إلى الاختبار.
هنا يأتي TDD (التطوير القائم على الاختبار) في اللعبة.
حتى لو قررت عدم استخدام هذه التقنية، عليك على الأقل أن تعرف ما الذي نتحدث عنه.
لنقم أولاً بإنشاء اختبار CarTest
الذي يجب أن يختبر ما إذا كانت الطريقة get_price
تُرجع السلسلة '€ 14.500'
. ثم قم بإنشاء Class Car
واكتب الطريقة get_price
التي تستوفي الاختبار. لا تبدأ بالتنفيذ
في هذه المرحلة، اسمحوا لي أن أقدم أيضًا نمط الاختبار AAA (تأكيد قانون الترتيب) المقبول على نطاق واسع في TDD . فهو يصف كيفية ترتيب الاختبار وهو مشابه جدًا لـ GWT (يُعطى متى ثم) من BDD (التطوير المبني على السلوك).
يمكنك اختبار فصولك الدراسية إذا كانت استثناءات في ظروف معينة. لننفذ الآن طريقة get_price
.
ما عليك سوى إنشاء Registry
فئة يقوم بتعيين قيمة مختلطة كعنصر مسمى في صفيف داخلي. استخدم الطريقة set()
أو الطريقة السحرية __set()
لهذا الغرض. بادئ ذي بدء، نفترض أنه يمكننا تمرير كائن JSON إلى فئة Car
الخاصة بنا. هذا سيعطي صفنا قيمة أكبر قليلاً.
طريقة أخرى get
أو __get()
يجب أن تتحقق من وجود عنصر ذو عنصر معين وإعادته عند النجاح. إذا لم يكن هناك عنصر من هذا القبيل، فقم بطرح OutOfBoundsException
. اكتب الآن مُنشئًا يتعامل مع إدخال JSON ويخزن الكائن في member-var data
. يجب أن تأخذ طريقة get_price
- السعر من data
var وتعتني بالمخرجات المنسقة.
تحقق من الخطوة 10 للفرع إذا كنت تواجه صعوبة في كتابة الرمز! يجب أن يكون price
المتغير عددًا صحيحًا. من المحتمل ألا تكون هذه مشكلة في الوقت الحالي لأنه يمكنك استخدام وظيفة PHP- number_format()
لإنشاء المخرجات الصحيحة. لكن في تثبيت WordPress، ستتوقع تعيين الإعدادات المحلية، على it_IT
(الإيطالية) على سبيل المثال.
الطريقة الصحيحة لتنسيق الأرقام في WordPress هي استخدام الدالة number_format_i18n()
.
لذلك دعونا نغير ذلك ونرى ما سيحدث:
Error: Call to undefined function wcctanumber_format_i18n()
سوف نقوم بإصلاح هذا في ثانية، ولكن دعونا نجهز هذا قليلاً أولاً. يستخدم Brain Monkey setUp()
و tearDown()
التي توفرها PHPUnit . يمكنك تجاوز تلك الأساليب. لنقم بإنشاء TestCase
مخصصًا - أطلق عليه اسم WcctaCase
- والذي يمكننا توسيعه لأننا سنفعل ذلك على الأرجح في كل فئة اختبار.
لنقم الآن بتضمين مساحة الاسم للاختبارات في قسم التحميل التلقائي:
"autoload-dev": {
"psr-4": {
"tests\wccta\": "tests/wccta"
}
},
أخيرًا، دعونا نغير أصل فصول الاختبار لدينا.
class CarTest extends WcctaTestCase { // ... }
نحن على استعداد للسخرية من وظيفة WordPress الأولى لدينا
Functionsexpect( $name_of_function )->andReturn( $value );
إن كتابة اختبار لتوقع واحد فقط يبدو مجهودًا كبيرًا. ماذا لو كنت تريد الاختبار مقابل قيم مختلفة؟
مزود البيانات للإنقاذ. لقد تحدثت بالفعل عن التعليقات التوضيحية في الخطوة 5. وهذا أيضًا مفيد جدًا:
@dataprovider method_that_returns_data
إلقاء نظرة على المثال الخاص بي. تقوم getData
بإرجاع مجموعة من المصفوفات. تحتوي كل من هذه المصفوفات على 3 قيم. لا يمكن لطريقة test_getPrice
الخاصة بنا قبول موفر البيانات مع التعليق التوضيحي فحسب، بل يمكنها أيضًا تحديد input-vars كمعلمات.
يمكنك اختبار فصولك الدراسية إذا كانت استثناءات في ظروف معينة.
ما عليك سوى إنشاء Registry
فئة يقوم بتعيين قيمة مختلطة كعنصر مسمى في صفيف داخلي. استخدم الطريقة set()
أو الطريقة السحرية __set()
لهذا الغرض.
هناك طريقة أخرى get
أو __get()
يجب أن تتحقق من وجود عنصر يحتوي على مفتاح معين وتعيده عند النجاح. إذا لم يكن هناك عنصر من هذا القبيل، قم بطرح OutOfBoundsException
.
تحقق من الخطوة 10 للفرع إذا كنت تواجه صعوبة في كتابة الرمز!
الخطوات الأخيرة أوصلتنا إلى المصانع . ما هو المصنع؟ في بعض الأحيان تقوم بإنشاء وظائف أو أساليب تخفي ببساطة العملية المعقدة لإنشاء كائن معين. وفي بعض الأحيان يتعين عليك أن تقرر نوع الكائن الذي تريد إنشاءه.
في مكونات WordPress الإضافية أفضّل إضافة خطافات في المصانع إلى الكائنات. هناك مكونات إضافية تضيف خطافات في منشئي الفصل. هذا ليس بالأمر الجيد (خاصة عندما لا تزال تختبر الطريقة الكلاسيكية - إنشاء بيئة كاملة باستخدام WordPress وتشغيله).
لنقم بإنشاء Factory
فئة باستخدام وظيفة ثابتة تسمى create
. يجب أن تقوم هذه الطريقة بإرجاع كائن Car
. ولكن دعونا نعيد بناء مُنشئ Car
بحيث يتوقع بالفعل كائنًا ولا يتوقع سلسلة JSON. سنفعل ذلك باستخدام طريقة الإنشاء لفئة Factory
بدلاً من ذلك.
اختبر مكونك الإضافي الآن باستخدام composer test
وسترى بعض الأخطاء:
TypeError: Argument 1 passed to wcctaCar::__construct() must be an object, string given, called in ...
وعلينا أن نصحح اختباراتنا أيضا..
ممتاز! دعونا ننشئ اختبارًا لمصنعنا. سنترك الطريقة بدون أي محتوى في الوقت الحالي. قم بإجراء الاختبارات مرة أخرى!
There was 1 risky test:
1) testswcctaFactoryTest::test_create
This test did not perform any assertions
تم اجتياز الاختبارات ولكنك حصلت على رسالة مفادها أن هناك اختبارًا محفوفًا بالمخاطر. بالمناسبة: قم بتسمية الوظيفة test_create
فقط create
التعليق التوضيحي واستخدامه @test
. أعتقد أن استخدام هذا التعليق التوضيحي يعتمد على ذوقك الشخصي!
سوف نتعمق الآن في هذا الأمر قليلاً.
قم بإنشاء واجهة FooterInterface
التي تحدد info
الطريقة العامة التي لا تتوقع أي قيمة إرجاع. قم بتطبيق الواجهة في Car
، حيث يمكن info
- على سبيل المثال - إخراج رسالة مضحكة.
حدد نوع الإرجاع FooterInterface
لطريقة create
Factory
وأضف طريقة info
Car
إلى WordPress-Action wp_footer
.
الآن دعونا نختبر ذلك في FactoryTest
. هناك طريقتان على الأقل لاختبار ذلك بشكل صحيح. استخدم has_action أو ActionsexpectAdded()
. سيكون اختبار المرشحات مشابهًا ويتم وصفه جيدًا في الصفحة المرتبطة.
تحقق مما إذا كان composer test
لا يزال يجتاز جميع الاختبارات.
كيف هي التغطية الآن؟ قم بتنفيذ composer coverage
وتحقق من المخرجات التي تم إنشاؤها.
لا يتم تغطية طريقة info
بفئة Car
بأي اختبار. ولكن هل يمكننا اختبار مخرجات الطريقة؟
اتضح أن الأمر سهل للغاية مع توقعOutputString.
دعونا نحتفل بما تعلمناه!
قم بإنشاء Locale
فئة تحتوي على طريقة عامة get
تُرجع get_locale()
. استبعاد الطريقة من التغطية!
الآن قم بإنشاء مُنشئ في فئة Plugin
لدينا التي تقبل Locale
-instance وقم بتخزينه في member-var $this->locale
. قم بإنشاء طريقة get_region_code
التي تُرجع قيمة $this->locale->get()
. اه، وقم بإزالة طريقة is_loaded
. ;)
في اختبارنا، يمكننا إنشاء كائن من النوع Locale
، والاستهزاء بوظيفة get_locale
في WordPress وتمريرها إلى مُنشئ Plugin
! لكني أريد استخدام Mocker هنا:
public function test_get_region_code() {
$code = 'it_IT';
$locale = Mockery::mock( Locale::class );
$locale->shouldReceive( 'get' )->andReturn( $code );
$sut = new Plugin( $locale );
$this->assertEquals( $code, $sut->get_region_code() );
}
الآن يمكنك الذهاب وجعل ملحقات WordPress الخاصة بك مقاومة للرصاص!
استمتع!