【问题标题】:How to unit test functions that use type hinting如何对使用类型提示的函数进行单元测试
【发布时间】:2013-12-25 03:42:20
【问题描述】:

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

class Testable 
{
    function foo (Dependency $dependency) 
    {

    }
}

我想用这个代码对这个类Testable进行单元测试:

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


$testable = new Testable($dependencyMock);

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

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

如何使用 PHPUnit 对这个函数进行单元测试并且仍然存根 $dependency

【问题讨论】:

  • 在您创建模拟的位置显示您的测试代码。
  • 你确定Dependency 是一个存在的类吗?它不应该有一个命名空间吗?
  • 在使用模拟时尝试使用完整的命名空间,如 \some\name\space\Dependency,它应该可以修复
  • 谢谢!使用命名空间可以修复错误。
  • @Shakil 您应该将您的评论移至答案,以便它可以被接受。

标签: unit-testing symfony mocking phpunit type-hinting


【解决方案1】:

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

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

【讨论】:

  • 谢谢。我认为您需要完整的命名空间这一事实在文档中并不明确。看到您的帖子后,我想“哦!当然”,但我没有从文档中得到。
【解决方案2】:

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

SomeClass::class

所以在 OP 的例子中:

$dependencyMock = $this->getMockBuilder(Dependency::class)
    ->disableOriginalConstructor()
    ->getMock();
$testable = new Testable($dependencyMock);

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

【讨论】:

  • 这比其他答案更好(在 PHP 5.4+ 中)。 ::class 是确保使用正确类的方法
  • 有了这个答案,我终于明白了。非常感谢!
【解决方案3】:

我对夏基尔回答的解释:

我遇到了同样的问题。

按照 symfony2 食谱,我创建了一个模拟

\Doctrine\Common\Persistence\ObjectManager

我的服务构造函数是:

use Doctrine\ORM\EntityManager;

/* ... */

public function __construct(EntityManager $oEm)
{
    $this->oEm = $oEm;
}

所以我创建了我的单元测试(遵循 symfony2 食谱):

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

$myService = new MyService($entityManager);

然后我遇到了错误:

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

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

所以经过一番研究,类 Mock_ObjectManager_f4068b7f 实际上是一个动态类扩展了你的模拟类(在我的例子中是 Doctrine\ORM\EntityManager),所以类型提示不是问题并且效果很好。

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

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

$myService = new MyService($entityManager);

我只是从单元测试开始,所以你可能会发现我的解释很明显:p

【讨论】:

    猜你喜欢
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 2021-01-29
    • 1970-01-01
    相关资源
    最近更新 更多