2010-03-01 3 views
38

Я ищу лучший способ идти о тестировании следующего статического метода (в частности, с использованием Doctrine Model):PHPUnit Mock объектов и статические методы

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

В идеале я хотел бы использовать фиктивный объект для того, чтобы «fromArray» (с предоставленными пользовательскими данными) и «save» были вызваны, но это невозможно, поскольку метод является статическим.

Любые предложения?

ответ

42

Себастьян Бергманн, автор PHPUnit, недавно разместил сообщение в блоге о Stubbing and Mocking Static Methods. С помощью PHPUnit 3.5 и PHP 5.3, а также последовательным использованием в последнее время статической привязки, вы можете сделать

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

Update:staticExpects является deprecated as of PHPUnit 3.8 и будет полностью удален с более поздними версиями.

+11

Стоит отметить " Этот подход работает только для укусов и издевательств вызовов статических методов, когда вызывающий и вызываемый находятся в одном классе. Это связано с тем, что [статические методы смертность к испытанию] (http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/). " –

+1

Функция 'staticExpects' была удалена с PHPUnit v4. См. [Эту тему в github] (https://github.com/sebastianbergmann/phpunit-mock-objects/issues/137) для объяснения причин. –

+4

Как мы знаем, что 'staticExpects' был завершен, удаленный из последней версии PHPUnit, тогда каков альтернативный способ достичь этого без' staticExpects'? –

0

Тестирование статических методов обычно считается немного сложным (как вы, вероятно, уже заметили), особенно перед PHP 5.3.

Не могли бы вы изменить свой код, чтобы не использовать статический метод? Я действительно не понимаю, почему вы используете статический метод здесь; возможно, это может быть переписано на некоторый нестатический код, не так ли?


Например, может что-то вроде этого не сделать трюк:

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

Не уверен, что вы будете испытывать; но, по крайней мере, не статический метод больше ...

+0

Спасибо за предложение, это больше стиль, чем что-либо. Я мог бы сделать метод нестационарным в этом конкретном экземпляре (хотя я бы предпочел использовать его без экземпляра). –

+11

Вопрос определенно о насмешливых статических методах - рассказывая автору о том, что «не использовать статические методы» не разрезает горчицу. – Lotus

10

Там нет теперь библиотека 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'); 
+7

Эта библиотека золотая! Но я думаю, что они должны поставить на их страницу отказ от ответственности: «Просто потому, что вы можете тестировать глобальные функции и статические методы в нашей библиотеке, это не значит, что вы должны написать новый код таким образом». Я где-то читал, что плохой тест лучше, чем вообще не иметь тестов, и с помощью этой библиотеки вы можете добавить систему безопасности в свой устаревший код. Просто не забудьте написать новый код в лучшем виде :) – pedromanoel

0

Другой возможный подход с 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'))); 
1

Я бы сделал новый класс в пространстве имен тестов, который расширяет Model_User и проверяет это. Вот пример:

Оригинальный класс:

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

Mock класса для вызова в тестовый модуль (ы):

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); 
    } 
} 
+0

Я использую этот метод, когда я не хочу включать дополнительную библиотеку PHP, чтобы сделать это для меня. – b01

Смежные вопросы