2010-03-01 59 views
38

我找去有关测试下面的静态方法(特别是使用主义模型)的最佳方式: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的作者Sebastian Bergmann最近有一篇关于Stubbing and Mocking Static Methods的博客文章。使用PHPUnit 3.5和PHP 5.3以及一贯使用的后期绑定的,你可以做

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

更新:staticExpectsdeprecated as of PHPUnit 3.8并且将在以后的版本完全去除。

+11

值得注意“这种方法只适用于调用者和被调用者在同一个类中的静态方法调用的存根和模拟,这是因为[静态方法是死于可测性](http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/)。“ – 2012-10-01 15:30:59

+1

从PHPUnit v4开始,'staticExpects'函数已被删除。请参阅[在github上的此线程](https://github.com/sebastianbergmann/phpunit-mock-objects/issues/137)以解释原因。 – 2015-11-28 00:51:35

+4

因为我们知道'staticExpects'已经从PHPUnit的最新版本中移除了,那么在没有'staticExpects'的情况下实现这一点的替代方法是什么? – 2017-01-17 13:13:51

0

测试静态方法通常被认为是有点困难(您可能已经注意到),特别是在PHP 5.3之前。

难道你不能修改你的代码不使用静态方法吗?事实上,我不明白你为什么在这里使用静态方法。这可能会重新写入一些非静态代码,难道不是吗?


例如,可这样的事情不会做的伎俩:

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

不知道你会什么测试;但是,至少,没有静态方法了...

+0

感谢您的建议,它比任何东西都更具风格。我可以使这个方法在这个特定的实例中是非静态的(尽管我希望能够在不实例化的情况下使用它)。 – 2010-03-01 18:02:53

+11

这个问题肯定是关于嘲弄静态方法 - 告诉作者“不使用静态方法”不会削减芥末。 – Lotus 2014-10-02 14:09:18

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 2015-03-04 13:06:27

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(); 
    } 
} 

模拟类调用在单元测试(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. 
} 

在你的单元测试:

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 2017-11-19 13:49:40

相关问题