En PHPUnit, comment se moquer d'une fonction qui ne fait pas partie d'une classe ?
P粉216807924
P粉216807924 2023-10-29 15:44:54
0
2
714

Le projet sur lequel je travaille actuellement contient un mélange de code PHP orienté objet et procédural. J'ai donc quelque chose comme ça :

function doStuff($value)
{
    $x = $value + 1;

    return $x;
}

class MyClass
{
    private $field;

    public function setMyValue($amount)
    {
        $this->field = doStuff($amount) + doStuff(2 * $amount);
    }
}

Il existe quelques-unes de ces dépendances, mais très peu (on peut les compter sur une main). Cependant, j'ai besoin d'écrire des tests unitaires pour la classe (en utilisant PHPUnit) et je ne sais pas comment simuler les fonctions du terminal (dans ce cas doStuff). Autant que je sache, la fonctionnalité moqueuse de PHPUnit ne fonctionne qu'avec les classes.

Je l'aurais fait sans aucune moquerie, mais le problème est que certaines de ces fonctions effectuent des opérations d'E/S ; je ne pense pas que ce soit une bonne idée de ne pas s'en moquer d'une manière ou d'une autre.

Comment puis-je résoudre ce problème ?

P粉216807924
P粉216807924

répondre à tous(2)
P粉275883973

La seule option que je vois est l'injection de dépendances puisque votre classe souhaite utiliser des ressources en dehors de la classe. Cela enfreint donc certaines règles d'encapsulation.

Comment j'ai fait cela dans le passé est de placer ces fonctions dans leur propre classe et de les exiger/inclure, et lors de la définition des variables de test, d'inclure un fichier de base avec la même fonction semi-"simulée" qui renvoie un état connu .

Mon autre approche consiste à créer une classe UTILITY simple qui contient toutes ces fonctions de données, puis à utiliser l'injection de dépendances et la simulation pour la tester.

class Utilities
{

    function doStuff($value)
    {
        $x = $value + 1;
        return $x;
    }
}

class MyClass
{
    private $UtilitiesObject;

    private $field;

    public function setMyValue($amount)
    {
//        $this->field = doStuff($amount) + doStuff(2 * $amount);
        $this->field = $this->UtilitiesObject->doStuff($amount) + $this->UtilitiesObject->doStuff(2 * $amount);
    }
}

    // Constructor Injection, pass the Utilities object here
    public function __construct($Utilities = NULL)
    {
        if(! is_null($Utilities) )
        {
            if($Utilities instanceof Utilities)
            {
                $this->SetUtilities($Utilities);
            }
        }
    }

    function SetUtilities(Utilities $Utilities)
    {
        $this->UtilitiesObject = $Utilities
    }

}

Test :

class UtilitiesTest extends PHPUnit_Framework_TestCase
{

    // Could also use dataProvider to send different returnValues, and then check with Asserts.
    public function testSetMyValue()
    {
        // Create a mock for the Utilities class,
        // only mock the doStuff() method.
        $MockUtilities = $this->getMock('Utilities', array('doStuff'));

        // Set up the expectation for the doStuff() method 
        $MockUtilities->expects($this->any())
                    ->method('doStuff')
                    ->will($this->returnValue(1));

        // Create Test Object - Pass our Mock as the Utilities
        $TestClass = new MyClass($MockUtilities);
        // Or
        // $TestClass = new MyClass();
        // $TestClass->SetUtilitiess($MockUtilities);

        // Test doStuff
        $amount = 10;   // Could be checked with the Mock functions
        $this->assertEquals(2, $TestClass->doStuff($amount));       // Mock always returns 1, so 1+1=2
    }
}
P粉155832941

Vous pouvez profiter de la Politique de repli de l'espace de noms de PHP lorsque vous appelez des fonctions à partir d'un espace de noms (défini dans l'espace de noms global) et que vous les appelez toujours des fonctions non qualifiées.

Cela vous permet de créer des simulations en fournissant des fonctions dans l'espace de noms de l'appelant.

Pour vous faciliter la vie, je l'ai intégré dans la bibliothèque php-mock-phpunit qui peut être utilisée avec PHPUnit :

namespace foo;

use phpmock\phpunit\PHPMock;

class BuiltinTest extends \PHPUnit_Framework_TestCase
{

    use PHPMock;

    public function testTime()
    {
        $time = $this->getFunctionMock(__NAMESPACE__, "time");
        $time->expects($this->once())->willReturn(3);

        $this->assertEquals(3, time());
    }
}
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal