如何单元测试使用类型提示的函数

use*_*816 35 phpunit unit-testing mocking type-hinting symfony

假设我有一个类包含一个使用类型提示的函数,如下所示:

class Testable 
{
    function foo (Dependency $dependency) 
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

我想Testable使用以下代码对此类进行单元测试:

$dependencyMock = $this->getMockBuilder('Dependency')
        ->disableOriginalConstructor()
        ->getMock();


$testable = new Testable($dependencyMock);
Run Code Online (Sandbox Code Playgroud)

如果我使用PHPUnit创建$ dependency的存根,然后尝试foo使用这个模拟调用函数(如上所述),我将得到一个致命的错误,说:

传递给函数foo()的参数1必须是Dependency的一个实例,给出Mock_Foo的实例

如何使用PHPUnit和仍然存根来对此函数进行单元测试$dependency

Sha*_*kil 43

使用模拟时使用完整命名空间,它将修复嘲弄继承问题.

$dependencyMock = $this->getMockBuilder('\Some\Name\Space\Dependency')
    ->disableOriginalConstructor()
    ->getMock();
$testable = new Testable($dependencyMock);
Run Code Online (Sandbox Code Playgroud)


Gaz*_*dge 19

在PHP 5.5+中使用完整命名空间的最简单方法是使用类静态方法:

SomeClass::class
Run Code Online (Sandbox Code Playgroud)

所以在OP的例子中:

$dependencyMock = $this->getMockBuilder(Dependency::class)
    ->disableOriginalConstructor()
    ->getMock();
$testable = new Testable($dependencyMock);
Run Code Online (Sandbox Code Playgroud)

这使得使用IDE进行重构变得更加容易

  • 这比其他答案更好(在PHP 5.4+中).`:: class`是确保使用正确类的方法 (2认同)

Alc*_*lyn 5

我对Shakil答案的解释是:

我有同样的问题.

在symfony2食谱之后,我创建了一个模拟器

\Doctrine\Common\Persistence\ObjectManager
Run Code Online (Sandbox Code Playgroud)

我的服务构造函数是:

use Doctrine\ORM\EntityManager;

/* ... */

public function __construct(EntityManager $oEm)
{
    $this->oEm = $oEm;
}
Run Code Online (Sandbox Code Playgroud)

所以我创建了我的单元测试(在symfony2 cookbook之后):

$entityManager = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
    ->disableOriginalConstructor()
    ->getMock();

$myService = new MyService($entityManager);
Run Code Online (Sandbox Code Playgroud)

然后我有错误:

Argument 1 passed to MyService::__construct() must be an instance of Doctrine\ORM\EntityManager, instance of Mock_ObjectManager_f4068b7f given
Run Code Online (Sandbox Code Playgroud)

首先,我认为类型提示与单元测试不兼容,因为模拟实例被传递给构造函数而不是EntityManager的实例.

所以在经过一些研究之后,类Mock_ObjectManager_f4068b7f实际上是一个动态类,它扩展了mock的类(在我的例子中是Doctrine\ORM\EntityManager),所以类型提示不是问题而且效果很好.

我的解决方案是创建一个Doctrine\ORM\EntityManager而不是\ Doctrine\Common\Persistence\ObjectManager的模拟:

$entityManager = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
    ->disableOriginalConstructor()
    ->getMock();

$myService = new MyService($entityManager);
Run Code Online (Sandbox Code Playgroud)

我刚开始进行单元测试,所以你可能会发现我的解释明显:p