Le package permet de simuler les fonctions internes de PHP aussi simplement que possible. Utilisez ce package lorsque vous avez besoin de simuler des fonctions telles que : time()
, str_contains()
, rand
, etc.
Installation
Usage
L'exécution se moque
Maquette prédéfinie
Mélange de deux manières précédentes
PHPUnité 9
PHPUnit 10 et supérieur
Enregistrer une extension PHPUnit
Enregistrez des simulations
État
Suivi des appels
Fonctions d'espace de noms globales
Implémentation des fonctions internes
Fonctions internes
Restrictions
Fournisseurs de données
composer nécessite xepozz/internal-mocker --dev
L'idée principale est assez simple : enregistrez un écouteur pour PHPUnit et appelez d'abord l'extension Mocker.
Créer un nouveau fichier tests/MockerExtension.php
Collez le code suivant dans le fichier créé :
<?phpdeclare(strict_types=1);namespace AppTests;utilisez PHPUnitRunnerBeforeTestHook;utilisez PHPUnitRunnerBeforeFirstTestHook;utilisez XepozzInternalMockerMocker;utilisez XepozzInternalMockerMockerState;classe finale MockerExtension implémente BeforeTestHook, BeforeFirstTestHook {fonction publique exécuterBeforeFirstTest() : void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState :: saveState (); }fonction publique exécuterBeforeTest(string $test): void{ MockerState::resetState(); } }
Enregistrez le hook en tant qu'extension dans phpunit.xml.dist
<extensions> <extension class="AppTestsMockerExtension"/> </extensions>
Créer un nouveau fichier tests/MockerExtension.php
Collez le code suivant dans le fichier créé :
<?phpdeclare(strict_types=1);namespace AppTests;utiliser PHPUnitEventTestPreparationStarted;utiliser PHPUnitEventTestPreparationStartedSubscriber;utiliser PHPUnitEventTestSuiteStarted;utiliser PHPUnitEventTestSuiteStartedSubscriber;utiliser PHPUnitRunnerExtensionExtension;utiliser PHPUnitRunnerExtensionFacade;utiliser PHPUnitRunnerExtensionParameterCollection ; utiliser PHPUnitTextUIConfigurationConfiguration ; utiliser XepozzInternalMockerMocker ; utiliser XepozzInternalMockerMockerState ; la classe finale MockerExtension implémente l'extension {public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters) : void{$facade->registerSubscribers(new class () implémente StartedSubscriber {public function notify(Started $event) : void{ MockerExtension::load(); } },une nouvelle classe implémente PreparationStartedSubscriber {public function notify(PreparationStarted $event): void{ MockerState::resetState(); } }, ); }fonction statique publique load() : void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState :: saveState (); } }
Enregistrez le hook en tant qu'extension dans phpunit.xml.dist
<extensions> <bootstrap class="AppTestsMockerExtension"/> </extensions>
Ici, vous avez enregistré une extension qui sera appelée à chaque fois que vous exécuterez ./vendor/bin/phpunit
.
Par défaut, toutes les fonctions seront générées et enregistrées dans le fichier /vendor/bin/xepozz/internal-mocker/data/mocks.php
.
Remplacez le premier argument du constructeur Mocker
pour modifier le chemin :
$mocker = new Mocker('/path/to/your/mocks.php');
Le package prend en charge plusieurs façons de simuler des fonctions :
L'exécution se moque
Des simulations prédéfinies
Mélange de deux manières précédentes
Si vous souhaitez que votre scénario de test soit utilisé avec une fonction simulée, vous devez l'enregistrer au préalable.
Revenez au MockerExtension::executeBeforeFirstTest
créé et modifiez la variable $mocks
.
$ se moque = [ ['espace de noms' => 'AppService','nom' => 'heure', ], ];
Cette simulation proxy chaque appel de time()
sous l'espace de noms AppService
via un wrapper généré.
Lorsque vous souhaitez simuler le résultat des tests, vous devez écrire le code suivant dans le scénario de test requis :
MockerState::addCondition( 'AppService', // espace de noms 'time', // nom de la fonction [], //arguments 100 // résultat);
Vous pouvez également utiliser un rappel pour définir le résultat de la fonction :
MockerState::addCondition( '', // espace de noms 'headers_sent', // nom de la fonction [null, null], // les deux arguments sont des références et ils ne sont pas encore initialisés lors de l'appel de fonction fn ($file, &$line) => $file = $line = 123, // résultat du rappel);
Votre cas de test ressemblera donc à ce qui suit :
<?phpnamespace AppTests;utiliser AppService;utiliser PHPUnitFrameworkTestCase;class ServiceTest étend TestCase {public function testRun2() : void{$service = new Service(); MockerState::addCondition('AppService','heure', [],100);$this->assertEquals(100, $service->doSomething()); } }
Voir l'exemple complet dans XepozzInternalMockerTestsIntegrationDateTimeTest::testRun2
Les simulations prédéfinies vous permettent de vous moquer d'un comportement à l'échelle mondiale.
Cela signifie que vous n'avez pas besoin d'écrire MockerState::addCondition(...)
dans chaque scénario de test si vous souhaitez vous en moquer pour l'ensemble du projet.
Gardez à l'esprit que les mêmes fonctions de différents espaces de noms ne sont pas les mêmes pour
Mocker
.
Revenons donc au MockerExtension::executeBeforeFirstTest
créé et modifiez la variable $mocks
.
$ se moque = [ ['namespace' => 'AppService','name' => 'time','result' => 150,'arguments' => [], ], ];
Après cette variante, chaque AppServicetime()
renverra 150
.
Vous pouvez ajouter beaucoup de simulations. Mocker
compare les valeurs arguments
avec les arguments de la fonction appelante et renvoie le résultat nécessaire.
Mix signifie que vous pouvez utiliser une simulation prédéfinie dans un premier temps et une simulation d'exécution après.
Si vous utilisez Runtime mock
vous pouvez être confronté au problème suivant : après avoir simulé la fonction, vous l'avez toujours simulée dans d'autres cas de test.
MockerState::saveState()
et MockerState::resetState()
résolvent ce problème.
Ces méthodes enregistrent l’état « actuel » et déchargent chaque simulation Runtime mock
appliquée.
L'utilisation de MockerState::saveState()
après Mocker->load($mocks)
enregistre uniquement les simulations prédéfinies .
Vous pouvez suivre les appels de fonctions simulées en utilisant la méthode MockerState::getTraces()
.
$traces = MockerState::getTraces('AppService', 'time');
$traces
contiendra un tableau de tableaux avec la structure suivante :
[ ['arguments' => [], // arguments de la fonction'trace' => [], // le résultat de la fonction debug_backtrace'result' => 1708764835, // résultat de la fonction],// ... ]
Toutes les fonctions internes sont tronquées pour être compatibles avec celles d'origine. Cela oblige les fonctions à utiliser des arguments référencés ( &$file
) comme le font les originaux.
Ils se trouvent dans le fichier src/stubs.php
.
Si vous devez ajouter une nouvelle signature de fonction, remplacez le deuxième argument du constructeur Mocker
:
$mocker = new Mocker(stubPath : '/path/to/your/stubs.php');
La façon dont vous pouvez vous moquer des fonctions globales est de les désactiver dans php.ini
: https://www.php.net/manual/en/ini.core.php#ini.disable-functions
Le meilleur moyen est de les désactiver uniquement pour les tests en exécutant une commande avec les indicateurs supplémentaires :
php -ddisable_functions=${fonctions} ./vendor/bin/phpunit
Si vous utilisez PHPStorm, vous pouvez définir la commande dans la section
Run/Debug Configurations
. Ajoutez l'indicateur-ddisable_functions=${functions}
au champInterpreter options
.
Vous pouvez conserver la commande dans le fichier
composer.json
sous la sectionscripts
.
{ "scripts": {"test": "php -ddisable_functions=time,serialize,header,date ./vendor/bin/phpunit" } }
Remplacez
${functions}
par la liste des fonctions que vous souhaitez simuler, séparées par des virgules, par exemple :time,rand
.
Alors maintenant, vous pouvez également vous moquer des fonctions globales.
Lorsque vous désactivez une fonction dans php.ini
vous ne pouvez plus l'appeler. Cela signifie que vous devez le mettre en œuvre vous-même.
De toute évidence, presque toutes les fonctions implémentées en PHP sont identiques à celles de Bash.
Le moyen le plus court d'implémenter une fonction est d'utiliser la syntaxe `bash command`
:
$mocks[] = [ 'espace de noms' => '', 'nom' => 'heure', 'fonction' => fn () => `date +%s`, ];
Gardez à l'esprit que laisser une fonction globale sans implémentation entraînera un appel de recours de la fonction, ce qui entraînera une erreur fatale.
Parfois, vous pouvez être confronté à une situation désagréable lorsque la fonction moquée ne se moque pas sans forcer l'utilisation namespace
function
. Cela peut signifier que vous essayez de créer un fichier interpréteur PHP dans @dataProvider
. Soyez prudent et comme solution de contournement, je peux vous suggérer d'appeler le moqueur dans le constructeur du test. Déplacez donc d’abord tout le code de votre méthode d’extension executeBeforeFirstTest
vers une nouvelle méthode statique et appelez-la dans les méthodes executeBeforeFirstTest
et __construct
.
la classe finale MyTest étend PHPUnitFrameworkTestCase {fonction publique __construct(?string $name = null, array $data = [], $dataName = '') {AppTestsMockerExtension::load();parent::__construct($name, $data, $dataName); } /// ...}
la classe finale MockerExtension implémente BeforeTestHook, BeforeFirstTestHook {fonction publique exécuterBeforeFirstTest() : void{self::load(); }fonction statique publique load() : void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState :: saveState (); }fonction publique exécuterBeforeTest(string $test): void{ MockerState::resetState(); } }
Tout cela à cause de PHPUnit 9.5 et du système de gestion d'événements inférieur. La fonctionnalité du fournisseur de données commence à fonctionner avant tout événement, il est donc impossible de simuler la fonction au début de l'exécution.