حقن التبعية المسخر التلقائي للاختبار
تسمح لك injected Trait
بإنشاء فئات بسهولة مع الاستهزاء بجميع تبعياتها لأغراض الاختبار.
النمط التالي شائع جدًا:
A
مع تمرير تبعياتها (كائنات الخدمة) عبر المُنشئA
، مع التأكد من استدعائها كما هو متوقع. ينتهي الأمر بالكثير من منطق الاختبار إلى أن يكون نموذجيًا لبناء كائن. تهدف injected Trait
إلى إزالة هذا النموذج تمامًا، مما يتيح لك التركيز على ما يهم: الاختبارات نفسها.
استخدم composer
للحصول على أحدث إصدار (ربما ستحتاج إليه للتطوير فقط):
$ composer require sellerlabs/ injected --dev
لنفترض أننا نعمل على تطوير تطبيق ويب، ونريد إرسال بريد إلكتروني إلى المستخدمين عند قيامهم بالتسجيل. للحصول على مثال بسيط، لنفترض أن المستخدم يتم تعريفه بالكامل من خلال عنوان بريده الإلكتروني. عندما يقومون بالتسجيل، بطبيعة الحال، نريد أن نرسل لهم رسالة شكر عبر البريد الإلكتروني. علاوة على ذلك، نود أن نختبر أن رسائل البريد الإلكتروني يتم إرسالها بالفعل دون إرسالها فعليًا .
أولاً، دعونا نحدد خدمة البريد الإلكتروني:
class EmailService
{
public function email ( $ address , $ content )
{
// Send an email to $address with body $content
}
}
(في التطبيق الحقيقي، يرسل email
بريدًا إلكترونيًا - ولكننا لسنا مهتمين بالتنفيذ هنا!)
لنحدد أيضًا UserController
الذي يتعامل مع عملية التسجيل البسيطة للغاية:
class UserController
{
private $ service ;
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
public function signUp ( $ emailAddress )
{
$ this -> service -> email ( $ emailAddress , ' Thanks for signing up! ' );
return $ emailAddress ;
}
}
هنا، نقدم تبعية EmailService
من خلال المُنشئ، ونستخدمها أثناء عملية التسجيل (البسيطة للغاية).
لاختبار هذا الفصل، سيتعين علينا القيام بأحد أمرين:
EmailService
باستخدام شيء مثل Mockery
وتأكد من استدعاء email
باستخدام الوسائط المتوقعة. تسمح لك injected Trait
بتحقيق الخيار 2 دون ألم. دعنا نلقي نظرة:
use SellerLabs injected injected Trait ;
/**
* Class injected Example
*
* // 1. These are helpful annotations for IDEs and language tools
* @property MockInterface $service
* @method UserController make()
*
* @author Benjamin Kovach <[email protected]>
*/
class injected Example extends PHPUnit_Framework_TestCase
{
// 2. Use our trait
use injected Trait;
// 3. Provide the name of the class to test
protected $ className = UserController::class;
public function testSignUp ()
{
// 4. Make a controller with mocked dependencies
$ controller = $ this -> make ();
$ address = ' [email protected] ' ;
// 5. We can access any mocked dependency of the class as a property
$ this -> service -> shouldReceive ( ' email ' )
-> withArgs (
[
$ address ,
' Thanks for signing up! '
]
);
$ result = $ controller -> signUp ( $ address );
$ this -> assertEquals ( $ address , $ result );
}
}
يجب أن يكون لكل فئة تستخدم injected Trait
خاصية $className
، والتي يتم استخدامها لتحديد موقع الفئة التي يتم اختبارها. توفر injected Trait
طريقة عامة واحدة، make
، التي تبني كائنًا من هذا النوع، ولكنها تسخر من تبعياته وتحفظها كخصائص لفئة الاختبار نفسها.
لذلك، في testSignUp
، نقوم ببناء وحدة التحكم باستخدام make()
، مما يتيح لنا الوصول إلى كائن نوع EmailService
مستهزئ يسمى $service
. هذا لأنه تم تعريفه بهذه الطريقة في مُنشئ UserController
:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
طوال مدة حالة الاختبار، يرتبط متغير العضو $service
بـ EmailService
المستهزئ، مما يسمح لنا بوضع توقعات حول ما يحدث معه عند استدعاء طريقة signUp
الخاصة بوحدة التحكم. نحن نستخدم Mockery
من أجل إنشاء الكائنات التي يتم الاستهزاء بها. توجد بعض التعليقات التوضيحية في تعليق الفصل الدراسي، والتي تساعد في الإكمال التلقائي لـ IDE لهذه الفئات حيث يتم الإعلان عن الخصائص الوهمية ديناميكيًا.
هذا المثال موجود في tests/ injected Example.php
. لا تتردد في كزة حولها!
قد يبدو تأثير هذه السمة صغيرًا نسبيًا، ولكن عند العمل على تطبيقات كبيرة حيث تحتوي الفئات على تبعيات متعددة، فإن هذا يجعل الاختبار أسهل بكثير .