web-dev-qa-db-fra.com

Objets factices et méthodes statiques PHPUnit

Je cherche le meilleur moyen de tester la méthode statique suivante (en utilisant spécifiquement un modèle de doctrine):

class Model_User extends Doctrine_Record
{
    public static function create($userData)
    {
        $newUser = new self();
        $newUser->fromArray($userData);
        $newUser->save();
    }
}

Idéalement, j'utiliserais un objet fictif pour m'assurer que "fromArray" (avec les données utilisateur fournies) et "save" étaient appelés, mais ce n'est pas possible car la méthode est statique.

Aucune suggestion?

43
rr.

Sebastian Bergmann, l'auteur de PHPUnit, a récemment publié un article sur Stubbing and Mocking Static Methods . Avec PHPUnit 3.5 et PHP 5.3 ainsi que l’utilisation cohérente des liaisons statiques tardives, vous pouvez faire

$class::staticExpects($this->any())
      ->method('helper')
      ->will($this->returnValue('bar'));

Update:staticExpects est obsolète à partir de PHPUnit 3.8 et sera complètement supprimé des versions ultérieures.

43
Gordon

Il existe maintenant la bibliothèque AspectMock pour vous aider:

https://github.com/Codeception/AspectMock

$this->assertEquals('users', UserModel::tableName());   
$userModel = test::double('UserModel', ['tableName' => 'my_users']);
$this->assertEquals('my_users', UserModel::tableName());
$userModel->verifyInvoked('tableName'); 
10
treeface

Je créerais une nouvelle classe dans l'espace de noms de test unitaire qui étend Model_User et le tester. Voici un exemple:

Classe d'origine:

class Model_User extends Doctrine_Record
{
    public static function create($userData)
    {
        $newUser = new self();
        $newUser->fromArray($userData);
        $newUser->save();
    }
}

Classe factice à appeler en test (s):

use \Model_User
class Mock_Model_User extends Model_User
{
    /** \PHPUnit\Framework\TestCase */
    public static $test;

    // This class inherits all the original classes functions.
    // However, you can override the methods and use the $test property
    // to perform some assertions.
}

Dans votre test unitaire:

use Module_User;
use PHPUnit\Framework\TestCase;

class Model_UserTest extends TestCase
{
    function testCanInitialize()
    {   
        $userDataFixture = []; // Made an assumption user data would be an array.
        $sut = new Mock_Model_User::create($userDataFixture); // calls the parent ::create method, so the real thing.

        $sut::test = $this; // This is just here to show possibilities.

        $this->assertInstanceOf(Model_User::class, $sut);
    }
}
1
b01

Tester des méthodes statiques est généralement considéré comme un peu difficile (comme vous l'avez probablement déjà remarqué) , surtout avant PHP 5.3.

Ne pourriez-vous pas modifier votre code pour ne pas utiliser une méthode statique? Je ne vois pas vraiment pourquoi vous utilisez une méthode statique ici, en fait; cela pourrait probablement être ré-écrit dans un code non statique, n'est-ce pas?


Par exemple, quelque chose comme ceci pourrait-il ne pas faire l'affaire:

class Model_User extends Doctrine_Record
{
    public function saveFromArray($userData)
    {
        $this->fromArray($userData);
        $this->save();
    }
}

Pas sûr de ce que vous allez tester; mais, au moins, plus de méthode statique ...

0
Pascal MARTIN

La bibliothèque doublit pourrait également vous aider à tester des méthodes statiques:

/* Create a mock instance of your class */
$double = Doublit::mock_instance(Model_User::class);

/* Test the "create" method */
$double::_method('create')
   ->count(1) // test that the method is called once
   ->args([Constraints::isInstanceOf('array')]) // test that first argument is an array
   ->stub('my_value') // stub the method to return "myvalue"
0
gealex

Une autre approche possible est avec la bibliothèque Moka

$modelClass = Moka::mockClass('Model_User', [ 
    'fromArray' => null, 
    'save' => null
]);

$modelClass::create('DATA');
$this->assertEquals(['DATA'], $modelClass::$moka->report('fromArray')[0]);
$this->assertEquals(1, sizeof($modelClass::$moka->report('save')));
0