如何在单元测试时模拟在PHP中静态声明的方法

时间:2016-01-23 04:17:05

标签: php phpunit

我创建了以下示例测试用例:

<?php

abstract class Model
{
    //...

    public static function factory($data)
    {
        $className = get_called_class();
        $obj = new $className($data);
        return $obj;
    }
}

class User extends Model
{

}

class ExampleController
{
    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function create()
    {
        return $this->user->factory(array('name' => 'Jim'));
    }
}

class ExampleTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $user = new User(array('name' => 'Jim'));

        $modelStub = $this->getMockBuilder('User')
            ->disableOriginalConstructor()
            ->getMock();

        $modelStub
            ->method('factory')
            ->with(array('name' => 'Jim'))
            ->willReturn($user);

        $example = new ExampleController($modelStub);

        $this->assertEquals($user, $example->create());
    }
}

但是我收到以下错误:

1) ExampleTest::testSomething
PHPUnit_Framework_MockObject_BadMethodCallException:

当我删除static关键字时,我似乎工作正常,然后我的测试通过了。但是我希望我的Model类在其他情况下也允许调用某些方法而不必首先实例化:

// when instantiation is required
$userModel = new User();
$user = $userModel->factory(array('name' => 'Jim'));

// called statically, no initial instantiation required
$user = User::factory(array('name' => 'Jim'));

我遇到过这个博客,其中指出静态声明但动态调用的方法是可以的。但是,动态声明但静态调用的方法将引发STRICT错误 - http://www.lornajane.net/posts/2010/declaring-static-methods-in-php

之前我也使用过Laravel的Eloquent,似乎两种方法都可以调用:

// Eloquent example without initial instantiation is possible too
$user = User::find(1);

无论如何,无论我的代码是否有效,我都希望能够模拟静态声明的这些方法。看来PHPUnit似乎没有很好地处理静态方法(我读过有一个staticExpects方法,但现在已经弃用了PHPUnit 3.8)。所以我即将开始尝试一些替代测试框架(Codeception和AspectMock,PHPSpec,嘲弄),因为我对其他人没有多少经验。非常感谢关于这个问题的一些指示或关于此事的建议,因为它对我们公司的遗留应用程序的单元测试也非常有帮助,谢谢

2 个答案:

答案 0 :(得分:0)

答案是AspectMock。 该库为这些问题提供了答案:

你如何伪造time()函数来为每次测试调用产生相同的结果?有没有办法存根类的静态方法?你能在运行时重新定义一个类方法吗?

答案 1 :(得分:0)

在静态定义$this->user->factory方法之前,您无法致电factory。您应该将其更改为User::factory。您可以使用Moka

模拟此类静态方法
class ExampleController
{
    private $_userClass;

    public function __construct($userClass = 'User')
    {
        $this->_userClass = $userClass;
    }

    public function create()
    {
        return $this->_userClass::factory(array('name' => 'Jim'));
    }
}

class ExampleControllerTest extends \PHPUnit_Framework_TestCase
{
    public function testCreateReturnsUser()
    {
        $userClass = Moka::stubClass(null, ['::factory' => 'USER']);
        $controller = new ExampleController($userClass);
        $this->assertEquals('USER', $controller->create());

        $this->assertEquals( 
            [[['name' => 'Jib']], 
            $userClass::$moka->report('::factory')
        );
    }
}