تساعد الحزمة على محاكاة وظائف php الداخلية بشكل بسيط قدر الإمكان. استخدم هذه الحزمة عندما تحتاج إلى وظائف وهمية مثل: time()
و str_contains()
و rand
وما إلى ذلك.
تثبيت
الاستخدام
يسخر من وقت التشغيل
وهمية محددة مسبقا
مزيج من الطريقتين السابقتين
PHP الوحدة 9
PHPUnit 10 وما فوق
سجل ملحق PHPUnit
تسجيل وهمية
ولاية
تتبع المكالمات
وظائف مساحة الاسم العالمية
تنفيذ الوظيفة الداخلية
الوظائف الداخلية
قيود
موفري البيانات
يتطلب الملحن xepozz/internal-mocker --dev
الفكرة الرئيسية بسيطة جدًا: قم بتسجيل مستمع لـ PHPUnit واستدعاء ملحق Mocker أولاً.
قم بإنشاء ملف tests/MockerExtension.php
جديد
الصق الكود التالي في الملف الذي تم إنشاؤه:
<?phpdeclare(strict_types=1);namespace AppTests;use PHPUnitRunnerBeforeTestHook;use PHPUnitRunnerBeforeFirstTestHook;use XepozzInternalMockerMocker;use XepozzInternalMockerMockerState;الطبقة النهائية MockerExtension تنفذ BeforeTestHook, BeforeFirstTestHook {وظيفة عامة ExecuteBeforeFirstTest(): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); }الوظيفة العامة ExecuteBeforeTest(string $test): void{ MockerState::resetState(); } }
قم بتسجيل الخطاف كملحق في phpunit.xml.dist
<الامتدادات> <extension class="AppTestsMockerExtension"/> </الامتدادات>
قم بإنشاء ملف tests/MockerExtension.php
جديد
الصق الكود التالي في الملف الذي تم إنشاؤه:
<?phpdeclare(strict_types=1);namespace AppTests;استخدام PHPUnitEventTestPreparationStarted;استخدام PHPUnitEventTestPreparationStartedSubscriber;استخدام PHPUnitEventTestSuiteStarted;استخدام PHPUnitEventTestSuiteStartedSubscriber;استخدام PHPUnitRunnerExtensionExtension;استخدام PHPUnitRunnerExtensionFacade;استخدام PHPUnitRunnerExtensionParameterCollection;استخدام PHPUnitTextUIConfigurationConfiguration;استخدام XepozzInternalMockerMocker;استخدام XepozzInternalMockerMockerState;الطبقة النهائية MockerExtension تنفذ الامتداد {وظيفة عامة bootstrap(التكوين $configuration, Facade $facade, ParameterCollection $parameters): void{$facade->registerSubscribers(new class () تنفذ StartedSubscriber {public function notify(Started $event): void{ MockerExtension::load(); } }، فئة جديدة تطبق PreparationStartedSubscriber {إعلام الوظيفة العامة (PreparationStarted $event): void{ MockerState::resetState(); } }, ); }تحميل الوظيفة الثابتة العامة (): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); } }
قم بتسجيل الخطاف كملحق في phpunit.xml.dist
<الامتدادات> <bootstrap class="AppTestsMockerExtension"/> </الامتدادات>
لقد قمت هنا بتسجيل الامتداد الذي سيتم استدعاؤه في كل مرة تقوم فيها بتشغيل ./vendor/bin/phpunit
.
افتراضيًا، سيتم إنشاء جميع الوظائف وحفظها في ملف /vendor/bin/xepozz/internal-mocker/data/mocks.php
.
تجاوز الوسيطة الأولى لمنشئ Mocker
لتغيير المسار:
$mocker = new Mocker('/path/to/your/mocks.php');
تدعم الحزمة عدة طرق للسخرية من الوظائف:
يسخر من وقت التشغيل
نماذج محددة مسبقًا
مزيج من الطريقتين السابقتين
إذا كنت ترغب في استخدام حالة الاختبار الخاصة بك مع وظيفة مستهزئة، فيجب عليك تسجيلها من قبل.
ارجع إلى MockerExtension::executeBeforeFirstTest
الذي تم إنشاؤه وقم بتحرير المتغير $mocks
.
$ يسخر = [ ['مساحة الاسم' => 'AppService'،'name' => 'الوقت'، ]، ];
سيقوم هذا الوهمي بتوكيل كل مكالمة time()
ضمن مساحة الاسم AppService
من خلال غلاف تم إنشاؤه.
عندما تريد محاكاة نتائج الاختبارات، يجب عليك كتابة الكود التالي في حالة الاختبار المطلوبة:
MockerState::addCondition( 'AppService', // مساحة الاسم "الوقت"، // اسم الوظيفة []، // الحجج 100 // النتيجة)؛
يمكنك أيضًا استخدام رد الاتصال لتعيين نتيجة الوظيفة:
MockerState::addCondition( '', // مساحة الاسم 'headers_sent'، // اسم الوظيفة [null, null], // كلتا الوسيطتين عبارة عن مراجع ولم تتم تهيئتهما بعد عند استدعاء الوظيفة fn (&$file, &$line) => $file = $line = 123, // نتيجة رد الاتصال);
لذلك ستبدو حالة الاختبار الخاصة بك كما يلي:
<?phpnamespace AppTests;use AppService;use PHPUnitFrameworkTestCase;class ServiceTest يوسع TestCase {وظيفة عامة testRun2(): void{$service = new Service(); MockerState::addCondition('AppService','time', [],100);$this->assertEquals(100, $service->doSomething()); } }
انظر المثال الكامل في XepozzInternalMockerTestsIntegrationDateTimeTest::testRun2
تسمح لك المحاكاة المحددة مسبقًا بالسخرية من السلوك عالميًا.
هذا يعني أنك لا تحتاج إلى كتابة MockerState::addCondition(...)
في كل حالة اختبار إذا كنت تريد الاستهزاء بها للمشروع بأكمله.
ضع في اعتبارك أن نفس الوظائف من مساحات أسماء مختلفة ليست هي نفسها بالنسبة
Mocker
.
لذا، عد إلى MockerExtension::executeBeforeFirstTest
الذي تم إنشاؤه وقم بتحرير المتغير $mocks
.
$ يسخر = [ ['مساحة الاسم' => 'AppService','name' => 'الوقت','النتيجة' => 150,'الوسائط' => [], ]، ];
بعد هذا المتغير، سيعود كل AppServicetime()
150
.
يمكنك إضافة الكثير من السخرية. يقارن Mocker
قيم arguments
مع وسيطات وظيفة الاستدعاء ويعيد النتيجة المطلوبة.
يعني المزج أنه يمكنك استخدام نموذج محدد مسبقًا في البداية ونموذج وقت التشغيل بعد ذلك.
إذا كنت تستخدم Runtime mock
، فقد تواجه مشكلة أنه بعد السخرية من الوظيفة، لا يزال يتم السخرية منها في حالات اختبار أخرى.
MockerState::saveState()
و MockerState::resetState()
يحلان هذه المشكلة.
تقوم هذه الأساليب بحفظ الحالة "الحالية" وإلغاء تحميل كل نسخة Runtime mock
تم تطبيقها.
يؤدي استخدام MockerState::saveState()
بعد Mocker->load($mocks)
إلى حفظ النماذج المحددة مسبقًا فقط.
يمكنك تتبع استدعاءات الدوال التي تم الاستهزاء بها باستخدام طريقة MockerState::getTraces()
.
$traces = MockerState::getTraces('AppService', 'time');
سوف تحتوي $traces
على مصفوفة من المصفوفات ذات البنية التالية:
[ ['arguments' => [], // وسيطات الدالة'trace' => [], // نتيجة debug_backtrace function'result' => 1708764835, // نتيجة الدالة],// ... ]
جميع الوظائف الداخلية متوقفة لتكون متوافقة مع الوظائف الأصلية. فهو يجعل الوظائف تستخدم الوسائط المرجعية ( &$file
) كما تفعل النسخ الأصلية.
وهي موجودة في الملف src/stubs.php
.
إذا كنت بحاجة إلى إضافة توقيع دالة جديد، فتجاوز الوسيط الثاني لمنشئ Mocker
:
$mocker = new Mocker(stubPath: '/path/to/your/stubs.php');
الطريقة التي يمكنك من خلالها الاستهزاء بالوظائف العامة هي تعطيلها في php.ini
: https://www.php.net/manual/en/ini.core.php#ini.disable-functions
أفضل طريقة هي تعطيلها فقط للاختبارات عن طريق تشغيل أمر يحتوي على علامات إضافية:
php -ddisable_functions=${functions} ./vendor/bin/phpunit
إذا كنت تستخدم PHPStorm، فيمكنك تعيين الأمر في قسم
Run/Debug Configurations
. أضف العلامة-ddisable_functions=${functions}
إلى حقلInterpreter options
.
يمكنك الاحتفاظ بالأمر في ملف
composer.json
ضمن قسمscripts
.
{ "script": {"test": "php -ddisable_functions=time,serialize,header,date ./vendor/bin/phpunit" } }
استبدل
${functions}
بقائمة الوظائف التي تريد الاستهزاء بها، مفصولة بفواصل، على سبيل المثال:time,rand
.
والآن يمكنك الاستهزاء بالوظائف العالمية أيضًا.
عندما تقوم بتعطيل إحدى الوظائف في php.ini
فلن تتمكن من استدعائها بعد الآن. هذا يعني أنه يجب عليك تنفيذه بنفسك.
من الواضح أن جميع الوظائف التي يتم تنفيذها تقريبًا في PHP تبدو مماثلة لتلك الخاصة بـ Bash.
أقصر طريقة لتنفيذ دالة هي استخدام صيغة `bash command`
:
$mocks[] = [ 'namespace' => '', 'name' => 'time', 'function' => fn () => `date +%s`, ];
ضع في اعتبارك أن ترك دالة عامة بدون تنفيذ سيؤدي إلى استدعاء الدالة، مما سيؤدي إلى خطأ فادح.
في بعض الأحيان قد تواجه موقفًا مزعجًا عندما لا يتم الاستهزاء بالوظيفة التي يتم الاستهزاء بها دون فرض استخدام namespace
function
. قد يعني ذلك أنك تحاول إنشاء ملف مترجم PHP في @dataProvider
. كن حذرًا منه وكحل بديل، قد أقترح عليك استدعاء المستهزئ في مُنشئ الاختبار. لذا، قم أولاً بنقل جميع التعليمات البرمجية من طريقة الامتداد الخاصة بك executeBeforeFirstTest
إلى الطريقة الثابتة الجديدة واستدعائها في كل من طريقتي executeBeforeFirstTest
و__ __construct
.
الطبقة النهائية MyTest تمتد PHPUnitFrameworkTestCase {وظيفة عامة __construct(?string $name = null, array $data = [], $dataName = '') {AppTestsMockerExtension::load();parent::__construct($name, $data, $dataName); } /// ...}
الطبقة النهائية MockerExtension تنفذ BeforeTestHook، BeforeFirstTestHook {وظيفة عامة ExecuteBeforeFirstTest(): void{self::load(); }تحميل الوظيفة الثابتة العامة (): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); }الوظيفة العامة ExecuteBeforeTest(string $test): void{ MockerState::resetState(); } }
كل ذلك بسبب PHPUnit 9.5 ونظام إدارة الأحداث الأقل. تبدأ وظيفة موفر البيانات في العمل قبل أي أحداث، لذلك من المستحيل الاستهزاء بالوظيفة في بداية وقت التشغيل.