90

避免 phpunit 必须调用模拟对象的构造函数的方法是什么?否则我需要一个模拟对象作为构造函数参数,另一个用于那个等等。api似乎是这样的:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName = '', $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

我不让它工作。$callOriginalConstructor即使设置为 false ,它仍然会抱怨构造函数参数。

我在构造函数中只有一个对象,它是一个依赖注入。所以我认为我在那里没有设计问题。

4

7 回答 7

148

您可以使用getMockBuilder,而不仅仅是getMock

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

有关详细信息,请参阅PHPUnit 文档中的测试替身”部分。

虽然你可以这样做,但最好不要这样做。您可以重构您的代码,而不是需要注入一个具体的类(带有构造函数),您只依赖于一个接口。这意味着您可以模拟或存根接口,而无需告诉 PHPUnit 修改构造函数行为。

于 2011-06-08T10:11:39.187 回答
42

干得好:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName = 'MyMockSoapClient';
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);
于 2009-03-09T22:38:29.930 回答
7

这个问题有点老了,但是对于新访问者,你可以使用createMock方法(之前createTestDouble在v5.4.0中调用和引入)来完成。

$mock = $this->createMock($className);

正如您在下面从PHPUnit\Framework\TestCase类中提取的代码(in phpunit/src/framework/TestCase.php)中看到的那样,它基本上会创建一个模拟对象而不调用原始构造函数

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}
于 2020-05-04T15:32:59.487 回答
4

作为附录,我想将expects()调用附加到我的模拟对象,然后调用构造函数。在 PHPUnit 3.7.14 中,调用时返回disableOriginalConstructor()的对象实际上是一个对象。

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

不幸的是,在 PHP 5.4 中有一个他们没有使用的新选项:

ReflectionClass::newInstanceWithoutConstructor

由于这不可用,我不得不手动反映该类,然后调用构造函数。

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

请注意, if functionCallFromConstructis protected,您必须特别使用才能setMethods()模拟受保护的方法。例子:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods()必须在调用之前expect()调用。就个人而言,我在之后disableOriginalConstructor()但之前将其链接起来getMock()

于 2013-03-08T02:21:53.233 回答
2

或者,您可以向getMock添加一个参数以防止调用默认构造函数。

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName = '', callOriginalConstructor = FALSE);

不过,我认为 dave1010 的答案看起来更好,这只是为了完整性。

于 2014-07-21T07:37:09.843 回答
1

也许您需要创建一个存根作为构造函数参数传入。然后你可以打破这个模拟对象链。

于 2008-12-01T22:30:16.410 回答
0

PHPUnit 旨在调用模拟对象的构造函数;为防止这种情况,您应该:

  1. 将模拟对象作为依赖项注入到您无法模拟的对象中
  2. 创建一个测试类,扩展您尝试调用的不调用父构造函数的类
于 2008-12-08T21:08:18.367 回答