Skip to content

Commit

Permalink
Merge pull request mockery#638 from classmarkets/restore-phpunit-list…
Browse files Browse the repository at this point in the history
…ener

Restore the PHPUnit listener
  • Loading branch information
davedevelopment committed Oct 27, 2016
2 parents 5fa8c52 + dd0dda9 commit 8569c87
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 3 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

* Destructors (`__destruct`) are stubbed out where it makes sense
* Allow passing a closure argument to `withArgs()` to validate multiple arguments at once.
* `Mockery\Adapter\Phpunit\TestListener` has been removed because it
incorrectly marks some tests as risky.
* `Mockery\Adapter\Phpunit\TestListener` has been rewritten because it
incorrectly marked some tests as risky. It will no longer verify mock
expectations but instead check that tests do that themselves.

## 0.9.4 (XXXX-XX-XX)

Expand Down
34 changes: 33 additions & 1 deletion docs/reference/phpunit_integration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Composer generated autoloader file:
the file name is updated for all your projects.)

To integrate Mockery into PHPUnit and avoid having to call the close method
and have Mockery remove itself from code coverage reports, use this in you
and have Mockery remove itself from code coverage reports, use this in your
suite:

.. code-block:: php
Expand All @@ -87,6 +87,38 @@ An alternative is to use the supplied trait:
}
Mockery provides a PHPUnit listener that makes tests fail if
``Mockery::close()`` has not been called. It can help identify tests where
you've forgotten to include the trait or extend the ``MockeryTestCase``.

If you are using PHPUnit's XML configuration approach, you can include the
following to load the ``TestListener``:

.. code-block:: xml
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener"></listener>
</listeners>
Make sure Composer's or Mockery's autoloader is present in the bootstrap file
or you will need to also define a "file" attribute pointing to the file of the
``TestListener`` class.

If you are creating the test suite programmatically you may add the listener
like this:

.. code-block:: php
// Create the suite.
$suite = new PHPUnit_Framework_TestSuite();
// Create the listener and add it to the suite.
$result = new PHPUnit_Framework_TestResult();
$result->addListener(new \Mockery\Adapter\Phpunit\TestListener());
// Run the tests.
$suite->run($result);
.. caution::

PHPUnit provides a functionality that allows
Expand Down
64 changes: 64 additions & 0 deletions library/Mockery/Adapter/Phpunit/TestListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* Mockery
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://github.com/padraic/mockery/blob/master/LICENSE
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic@php.net so we can send you a copy immediately.
*
* @category Mockery
* @package Mockery
* @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

namespace Mockery\Adapter\Phpunit;

class TestListener extends \PHPUnit_Framework_BaseTestListener
{
/**
* endTest is called after each test and checks if \Mockery::close() has
* been called, and will let the test fail if it hasn't.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest(\PHPUnit_Framework_Test $test, $time)
{
if (!$test instanceof \PHPUnit_Framework_TestCase) {
// We need the getTestResultObject and getStatus methods which are
// not part of the interface.
return;
}

if ($test->getStatus() !== \PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
// If the test didn't pass there is no guarantee that
// verifyMockObjects and assertPostConditions have been called.
// And even if it did, the point here is to prevent false
// negatives, not to make failing tests fail for more reasons.
return;
}

try {
// The self() call is used as a sentinel. Anything that throws if
// the container is closed already will do.
\Mockery::self();
} catch (\LogicException $_) {
return;
}

$e = new \PHPUnit_Framework_ExpectationFailedException(sprintf(
"Mockery's expectations have not been verified. Make sure that \Mockery::close() is called at the end of the test. Consider using %s\MockeryPHPUnitIntegration or extending %s\MockeryTestCase.",
__NAMESPACE__,
__NAMESPACE__
));
$result = $test->getTestResultObject();
$result->addFailure($test, $e, $time);
}
}
75 changes: 75 additions & 0 deletions tests/Mockery/Adapter/Phpunit/TestListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
/**
* Mockery
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://github.com/padraic/mockery/blob/master/LICENSE
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic@php.net so we can send you a copy immediately.
*
* @category Mockery
* @package Mockery
* @subpackage UnitTests
* @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

class Mockery_Adapter_Phpunit_TestListenerTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
// We intentionally test the static container here. That is what the
// listener will check.
$this->container = \Mockery::getContainer();
$this->listener = new \Mockery\Adapter\Phpunit\TestListener();
$this->testResult = new \PHPUnit_Framework_TestResult();
$this->test = new \Mockery_Adapter_Phpunit_EmptyTestCase();

$this->test->setTestResultObject($this->testResult);
$this->testResult->addListener($this->listener);

$this->assertTrue($this->testResult->wasSuccessful(), 'sanity check: empty test results should be considered successful');
}

public function testSuccessOnClose()
{
$mock = $this->container->mock();
$mock->shouldReceive('bar')->once();
$mock->bar();

// This is what MockeryPHPUnitIntegration and MockeryTestCase trait
// will do. We intentionally call the static close method.
$this->test->addToAssertionCount($this->container->mockery_getExpectationCount());
\Mockery::close();

$this->listener->endTest($this->test, 0);
$this->assertTrue($this->testResult->wasSuccessful(), 'expected test result to indicate success');
}

public function testFailureOnMissingClose()
{
$mock = $this->container->mock();
$mock->shouldReceive('bar')->once();

$this->listener->endTest($this->test, 0);
$this->assertFalse($this->testResult->wasSuccessful(), 'expected test result to indicate failure');

// Satisfy the expectation and close the global container now so we
// don't taint the environment.
$mock->bar();
\Mockery::close();
}
}

class Mockery_Adapter_Phpunit_EmptyTestCase extends PHPUnit_Framework_TestCase
{
public function getStatus()
{
return \PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
}
}

0 comments on commit 8569c87

Please sign in to comment.