PHPUnit模拟对象和静态方法
我正在寻找最好的方法去testing下面的静态方法(特别是使用Doctrine模型):
class Model_User extends Doctrine_Record { public static function create($userData) { $newUser = new self(); $newUser->fromArray($userData); $newUser->save(); } }
理想情况下,我会使用一个模拟对象来确保“fromArray”(使用提供的用户数据)和“save”被调用,但这是不可能的,因为方法是静态的。
有什么build议么?
PHPUnit的作者Sebastian Bergmann最近发表了一篇关于Stubbing和Mocking Static Methods的博文。 随着PHPUnit 3.5和PHP 5.3以及一致的使用后期静态绑定,你可以做
$class::staticExpects($this->any()) ->method('helper') ->will($this->returnValue('bar'));
更新: 从PHPUnit 3.8开始 , staticExpects
已被弃用,并且将会被更高版本完全删除。
现在有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');
通常认为testing静态方法有点困难(你可能已经注意到了) ,特别是在PHP 5.3之前。
你可以不修改你的代码不使用静态方法? 事实上,我不明白你为什么在这里使用静态方法。 这可能会重新写入一些非静态的代码,不是吗?
例如,这样的事情可能不是诀窍:
class Model_User extends Doctrine_Record { public function saveFromArray($userData) { $this->fromArray($userData); $this->save(); } }
不知道你要testing什么 但是,至less,没有静态的方法了…
另一种可能的方法是使用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')));
我将在扩展Model_User并testing它的unit testing命名空间中创build一个新类。 这是一个例子:
原始类:
class Model_User extends Doctrine_Record { public static function create($userData) { $newUser = new self(); $newUser->fromArray($userData); $newUser->save(); } }
模拟类调用unit testing:
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 testing中:
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); } }