【问题标题】:phpunit avoid constructor arguments for mockphpunit 避免模拟的构造函数参数
【发布时间】:2010-09-21 17:23:42
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: php unit-testing phpunit


    【解决方案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();
    }
    

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

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

        有关详细信息,请参阅PHPUnit's documentation 中有关"Test Doubles" 的部分。

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

        【讨论】:

        • 这对我很有用。不过,它应该是示例 10.3。我试图编辑该帖子,但由于编辑太短而将其退回。
        • 谢谢@Lytithwyn。更新了答案。
        【解决方案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() 之前链接它。

        【讨论】:

        • 不知道这是否是代码异味,但这对我很有用,我只想谢谢你。
        【解决方案5】:

        给你:

            // 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。
        • 我在 testcase.php 中将默认值更改为 false。您会认为默认情况下它会设置为 false。模拟构造函数似乎很奇怪
        • 优秀的答案。正是我想要的
        【解决方案6】:

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

        1. 将模拟对象作为依赖项注入到您无法模拟的对象中
        2. 创建一个测试类,扩展您尝试调用但不调用父构造函数的类

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-04-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-28
            • 2017-12-28
            • 1970-01-01
            相关资源
            最近更新 更多