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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/279493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:12:51  来源:igfitidea点击:

phpunit avoid constructor arguments for mock

phpunit-testingphpunit

提问by dave1010

What is the way to avoid phpunit having to call the constructor for a mock object? Otherwise I would need a mock object as constructor argument, another one for that etc. The api seems to be like this:

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

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

I don't get it to work. It still complains about the constructor argument, even with $callOriginalConstructorset to false.

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

I only have one object in the constructor and it is a dependency injection. So I don't think I have a design problem there.

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

回答by dave1010

You can use getMockBuilderinstead of just getMock:

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

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

See the section on "Test Doubles"in PHPUnit's documentationfor details.

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

Although you can do this, it's much better to not need to. You can refactor your code so instead of a concrete class (with a constructor) needing to be injected, you only depend upon an interface. This means you can mock or stub the interface without having to tell PHPUnit to modify the constructor behaviour.

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

回答by Matthew Purdon

Here you go:

干得好:

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

回答by Steve Tauber

As an addendum, I wanted to attach expects()calls to my mocked object and then call the constructor. In PHPUnit 3.7.14, the object that is returned when you call disableOriginalConstructor()is literally an object.

作为附录,我想将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)

Unfortunately, in PHP 5.4 there is a new option which they aren't using:

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

ReflectionClass::newInstanceWithoutConstructor

ReflectionClass::newInstanceWithoutConstructor

Since this wasn't available, I had to manually reflect the class and then invoke the constructor.

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

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

Note, if functionCallFromConstructis protected, you specifically have to use setMethods()so that the protected method is mocked. Example:

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

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

setMethods()must be called before the expect()call. Personally, I chain this after disableOriginalConstructor()but before getMock().

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

回答by Hans Wouters

Alternatively you could add a parameter to getMockto prevent the calling of the default constructor.

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

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

Still, I think the answer of dave1010 looks nicer, this is just for the sake of completeness.

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

回答by Wesley Gon?alves

This question is a little old, but for new visitors, you can do it using the createMockmethod (previously called createTestDoubleand introduced in v5.4.0).

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

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

As you can see in the code below extracted from the PHPUnit\Framework\TestCaseclass (in phpunit/src/framework/TestCase.php), it will basically create a mock object without calling the original constructor.

正如您在下面从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();
}

回答by Glenn Moss

Perhaps you need to create a stub to pass in as the constructor argument. Then you can break that chain of mock objects.

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

回答by silfreed

PHPUnit is designed to call the constructor on mocked objects; to prevent this you should either:

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

  1. Inject a mock object as a dependency into the object you're having trouble mocking
  2. Create a test class that extends the class you're trying to call that doesn't call the parent constructor
  1. 将模拟对象作为依赖项注入您在模拟时遇到问题的对象中
  2. 创建一个测试类,该类扩展您尝试调用的不调用父构造函数的类