Я ищу лучший способ проверить следующий статический метод (в частности, с помощью модели Doctrine):
class Model_User extends Doctrine_Record { public static function create($userData) { $newUser = new self(); $newUser->fromArray($userData); $newUser->save(); } }
В идеале я бы использовал макет объекта, чтобы гарантировать, что «fromArray» (с предоставленными данными пользователя) и «сохранить» были вызваны, но это невозможно, поскольку метод является статическим.
Какие-либо предложения?
Себастьян Бергманн, автор PHPUnit, недавно опубликовал сообщение в блоге о Stubbing и Mocking Static Methods . С PHPUnit 3.5 и PHP 5.3, а также последовательное использование позднего статического привязки, вы можете сделать
$class::staticExpects($this->any()) ->method('helper') ->will($this->returnValue('bar'));
Обновление: staticExpects
устарело с PHPUnit 3.8 и будет полностью удалено с более поздними версиями.
В настоящее время библиотека 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');
Я бы сделал новый класс в пространстве имен тестов, который расширяет Model_User и проверяет это. Вот пример:
Оригинальный класс:
class Model_User extends Doctrine_Record { public static function create($userData) { $newUser = new self(); $newUser->fromArray($userData); $newUser->save(); } }
Mock Class для вызова в модульном тесте:
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); } }
Тестирование статических методов обычно считается немного сложным (как вы, вероятно, уже заметили) , особенно перед PHP 5.3.
Не могли бы вы изменить свой код, чтобы не использовать статический метод? Я действительно не понимаю, почему вы используете статический метод здесь; возможно, это может быть переписано на некоторый нестатический код, не так ли?
Например, что-то вроде этого не может сделать трюк:
class Model_User extends Doctrine_Record { public function saveFromArray($userData) { $this->fromArray($userData); $this->save(); } }
Не уверен, что вы будете тестировать; но, по крайней мере, никакого статического метода больше …
Другой возможный подход – с библиотекой 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')));