phpunit避免模拟的构造函数参数


85

如何避免phpunit调用模拟对象的构造函数?否则,我需要一个模拟对象作为构造函数参数,另一个用于该对象,以此类推。api看起来像这样:

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

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

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

Answers:


139

您可以使用getMockBuilder而不是getMock

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

参见节“测试双打”PHPUnit的文档的详细信息。

尽管您可以执行此操作,但最好不要这样做。您可以重构代码,因此无需依赖注入具体类(带有构造函数),而只需依赖接口即可。这意味着您可以模拟或存根接口,而不必告诉PHPUnit修改构造函数的行为。


这对我来说很棒。不过应该是示例10.3。我尝试编辑帖子,但因编辑太短而将其踢回。
马修(Matthew)

谢谢@Lytithwyn。更新了答案。
dave1010 2012年

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

这似乎几乎是我想要的。我想只用要模拟的类和$ callMockConstructor来调用getMock。怎么样?像这样:$ this-> getMock($ classToMock,$ callMockConstructor)。我唯一想到的就是进入PHPUnit的源并将其更改为default = false。
Gutzofter

1
我在testcase.php中将默认值更改为false。您可能会认为默认情况下会将其设置为false。
模拟

极好的答案。我一直在寻找
Hades

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

请注意,如果functionCallFromConstructprotected,则必须专门使用setMethods()以便模拟受保护的方法。例:

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

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


不知道这是否是代码异味,但这对我很有用,我只想感谢您。
devbanana

1

也许您需要创建一个存根作为构造函数参数传递。然后,您可以断开模拟对象链。


1

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

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

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


1

这个问题有点老了,但是对于新来访者,您可以使用createMock方法(createTestDouble在v5.4.0中先前称为和引入)进行操作。

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

从下面的代码中,您可以看到从PHPUnit\Framework\TestCase类中提取的代码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();
}

0

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

  1. 将模拟对象作为依赖项注入到模拟困难的对象中
  2. 创建一个测试类来扩展您尝试调用的类,该类不会调用父构造函数
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.