【发布时间】:2013-01-29 06:41:06
【问题描述】:
代码会解释一切:
<?php
class ATest extends PHPUnit_Framework_TestCase
{
public function testDestructorOnOriginalClass() {
$a = new A(); // It
unset($a); // works
echo " great!"; // great!
$this->expectOutputString('It works great!');
}
public function testDestructorOnMockedClass() {
$a = $this->getMock('A', array('someNonExistingMethod')); // It
unset($a); // works
echo " great!"; // great!
$this->expectOutputString('It works great!');
}
}
class A {
public function __construct()
{
echo "It";
}
public function __destruct()
{
echo " works";
}
}
和输出:
# phpunit ATest.php
PHPUnit 3.7.13 by Sebastian Bergmann.
.F
Time: 0 seconds, Memory: 3.50Mb
There was 1 failure:
1) ATest::testDestructorOnMockedClass
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'It works great!'
+'It great! works'
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
正如您在第二个测试中看到的那样,它以错误的顺序打印 works,可能是因为 phpunit 在某处存储了对 mock 的引用,并且在测试结束时调用了 __destruct()...好吧,我已经检查过 @987654325 @ 方法,实际上它存储了对模拟对象 ($this->mockObjects[] = $mockObject;) 的引用,有效地阻止了对象被破坏,因此永远不会调用 __destructor()。
/**
* Returns a mock object for the specified class.
*
* @param string $originalClassName
* @param array $methods
* @param array $arguments
* @param string $mockClassName
* @param boolean $callOriginalConstructor
* @param boolean $callOriginalClone
* @param boolean $callAutoload
* @param boolean $cloneArguments
* @return PHPUnit_Framework_MockObject_MockObject
* @throws PHPUnit_Framework_Exception
* @since Method available since Release 3.0.0
*/
public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE)
{
$mockObject = PHPUnit_Framework_MockObject_Generator::getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$cloneArguments
);
$this->mockObjects[] = $mockObject;
return $mockObject;
}
所以问题是 - 有没有办法防止这种情况发生?我认为在应该调用 __destruct() 时忽略它是不好的限制。
【问题讨论】:
-
这是一个理论问题,还是您真的依赖 SUT 中依赖类的析构函数行为?如果是这样,这可能是糟糕设计的标志。这个问题不应该真的成为问题。
-
目前我尝试模拟 PDO 类并模仿它在对象被破坏时结束数据库连接的行为(并且没有其他方法可以关闭连接) - 所以这不是我可以更改的类(除了扩展它,如果用
getMock()模拟不起作用,我可能会这样做)。 -
好吧,无论如何,PDO 是一个特例。如果你想模拟它,你需要先扩展它并覆盖构造函数,否则 MockBuilder 根本无法实例化它(假设你不想要一个实际的连接,否则你不会模拟它,你会吗?)
-
是的,没错,但其中还有很多内容需要解释。我只是好奇我在模拟对象后是否缺少
__destruct()的这种行为。感谢您的帮助。
标签: php unit-testing mocking phpunit destructor