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