wake-up-neo.com

PHPUnit-Mock-Objekte und statische Methoden

Ich bin auf der Suche nach der besten Möglichkeit, die folgenden statischen Methoden zu testen (insbesondere mit einem Doctrine-Modell):

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

Idealerweise würde ich ein Mock-Objekt verwenden, um sicherzustellen, dass "fromArray" (mit den bereitgestellten Benutzerdaten) und "save" aufgerufen wurden. Dies ist jedoch nicht möglich, da die Methode statisch ist.

Irgendwelche Vorschläge?

43
rr.

Sebastian Bergmann, der Autor von PHPUnit, hatte kürzlich einen Blogbeitrag über Stubbing und Mocking Static Methods . Mit PHPUnit 3.5 und PHP 5.3 sowie der konsequenten Verwendung der späten statischen Bindung können Sie dies tun

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

Update:staticExpects ist seit PHPUnit 3.8 - veraltet und wird in späteren Versionen vollständig entfernt.

43
Gordon

Es gibt jetzt die AspectMock-Bibliothek, die dabei hilft:

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

Ich würde eine neue Klasse in dem Unit-Test-Namespace erstellen, die den Model_User erweitert, und das testen. Hier ist ein Beispiel:

Ursprüngliche Klasse:

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

Scheinklasse zum Abrufen von Komponententests:

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.
}

In Ihrem Gerätetest:

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

Das Testen statischer Methoden wird im Allgemeinen als etwas hart angesehen (wie Sie wahrscheinlich bereits bemerkt haben), vor allem PHP 5.3.

Können Sie Ihren Code nicht ändern, um statische Methoden nicht zu verwenden? Ich verstehe nicht wirklich, warum Sie hier eigentlich eine statische Methode verwenden. Dies könnte wahrscheinlich in nicht-statischen Code umgeschrieben werden, oder?


Zum Beispiel könnte so etwas nicht den Trick tun:

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

Nicht sicher, was Sie testen werden; aber zumindest keine statische Methode mehr ...

0
Pascal MARTIN

Die Bibliothek doublit kann Ihnen auch helfen, statische Methoden zu testen:

/* 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

Ein anderer möglicher Ansatz ist die Bibliothek 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')));