Why doesn't phpunit run __destruct () in a class class and how to force it?

The code will explain everything:

<?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"; } } 

and conclusion:

 # 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. 

As you can see in the second test, it prints works in the wrong order, perhaps because phpunit keeps a link to mock somewhere and __destruct() is called at the end of the test ... Ok I already checked getMock() , and in fact it stores a reference to a mocked object ( $this->mockObjects[] = $mockObject; ), which effectively blocks the object from destruction and therefore __destructor() never called.

 /** * 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; } 

So the question is, is there a way to prevent this? Ignoring __destruct() when it should be called, I consider a bad constraint.

+6
source share
3 answers

You just turn off the local variable - you are not destroying the object itself.

The object is also stored by PHPUnit itself. So the link still exists, so your unset() does not result in __destruct() .

So, the current behavior cannot be changed. Open the error in phpunit problem tracker.

+1
source

The fact is that you will not pass any values ​​to the second parameter of the getMock method, PHPUnit will close all methods from the class you are mocking (including " __destruct ").

But if you specify at least one method (it may even not be an existing method), PHPUnit will close only those methods that you pass in the second argument.

So, if you want to save all methods, but you also want to create a mock, you should do it like this:

 $mock = $this->getMock('A', array('someNonExistingMethod')); 

If you change this line, the test should pass.

+1
source

If the clone object layout and unset it, __destruct() cloned object can be called during the test. This can help you test __destruct() .

Note that this method cannot prevent the __destruct() original mock object from being called at the end of the test.

0
source

All Articles