web-dev-qa-db-ja.com

PHPUnitモックオブジェクトと静的メソッド

次の静的メソッドをテストするための最善の方法を探しています(特にDoctrineモデル)を使用):

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

理想的には、モックオブジェクトを使用して、「fromArray」(提供されたユーザーデータ)と「save」が呼び出されることを確認しますが、メソッドは静的であるため不可能です。

助言がありますか?

45
rr.

PHPUnitの作者であるSebastian Bergmannは、最近、 Stubbing and Mocking Static Methods に関するブログ投稿を行いました。 PHPUnit 3.5およびPHP 5.3および遅延静的バインディングの一貫した使用により、次のことができます。

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

更新:staticExpectsPHPUnit 3.8で非推奨 であり、今後のバージョンでは完全に削除されます。

42
Gordon

これを支援するAspectMockライブラリがあります。

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'); 
11
treeface

Model_Userを拡張する単体テスト名前空間に新しいクラスを作成し、テストします。以下に例を示します。

元のクラス:

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

ユニットテストで呼び出すモッククラス:

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

ユニットテストでは:

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

静的メソッドのテストは、一般的に少し難しいと見なされます(おそらくお気づきかもしれませんが)、特にPHP 5.3。

静的メソッドを使用しないようにコードを変更できませんか?実際、なぜここで静的メソッドを使用しているのかわかりません。これはおそらくいくつかの非静的コードに書き直すことができますが、できませんか?


たとえば、次のようなことはできません:

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

何をテストするかわからない。しかし、少なくとも静的メソッドはもうありません...

0
Pascal MARTIN

doublit ライブラリは、静的メソッドのテストにも役立ちます。

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

別の可能なアプローチは 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')));