Skip to content

Commit

Permalink
feature #28931 [PhpUnitBridge] Added ClassExistsMock (ro0NL)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 4.3-dev branch (closes #28931).

Discussion
----------

[PhpUnitBridge] Added ClassExistsMock

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | #...   <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#10528

I've thought about this before, and bumped into it again when trying to test #28898

This PR allows to mock `class|interface|trait_exists` to enable specific code path testing

Commits
-------

62caec1 [PhpUnitBridge] Added ClassExistsMock
  • Loading branch information
fabpot committed Dec 10, 2018
2 parents 7dc1521 + 62caec1 commit a9694f7
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Symfony/Bridge/PhpUnit/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.3.0
-----

* added `ClassExistsMock`

4.1.0
-----

Expand Down
75 changes: 75 additions & 0 deletions src/Symfony/Bridge/PhpUnit/ClassExistsMock.php
@@ -0,0 +1,75 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit;

/**
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class ClassExistsMock
{
private static $classes = array();

/**
* Configures the classes to be checked upon existence.
*
* @param array $classes Mocked class names as keys (case sensitive, without leading root namespace slash) and booleans as values
*/
public static function withMockedClasses(array $classes)
{
self::$classes = $classes;
}

public static function class_exists($name, $autoload = true)
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \class_exists($name, $autoload));
}

public static function interface_exists($name, $autoload = true)
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \interface_exists($name, $autoload));
}

public static function trait_exists($name, $autoload = true)
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \trait_exists($name, $autoload));
}

public static function register($class)
{
$self = \get_called_class();

$mockedNs = array(substr($class, 0, strrpos($class, '\\')));
if (0 < strpos($class, '\\Tests\\')) {
$ns = str_replace('\\Tests\\', '\\', $class);
$mockedNs[] = substr($ns, 0, strrpos($ns, '\\'));
} elseif (0 === strpos($class, 'Tests\\')) {
$mockedNs[] = substr($class, 6, strrpos($class, '\\') - 6);
}
foreach ($mockedNs as $ns) {
foreach (array('class', 'interface', 'trait') as $type) {
if (\function_exists($ns.'\\'.$type.'_exists')) {
continue;
}
eval(<<<EOPHP
namespace $ns;
function {$type}_exists(\$name, \$autoload = true)
{
return \\$self::{$type}_exists(\$name, \$autoload);
}
EOPHP
);
}
}
}
}
119 changes: 119 additions & 0 deletions src/Symfony/Bridge/PhpUnit/Tests/ClassExistsMockTest.php
@@ -0,0 +1,119 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ClassExistsMock;

class ClassExistsMockTest extends TestCase
{
public static function setUpBeforeClass()
{
ClassExistsMock::register(__CLASS__);
}

protected function setUp()
{
ClassExistsMock::withMockedClasses(array(
ExistingClass::class => false,
'NonExistingClass' => true,
ExistingInterface::class => false,
'NonExistingInterface' => true,
ExistingTrait::class => false,
'NonExistingTrait' => true,
));
}

public function testClassExists()
{
$this->assertFalse(class_exists(ExistingClass::class));
$this->assertFalse(class_exists(ExistingClass::class, false));
$this->assertFalse(class_exists('\\'.ExistingClass::class));
$this->assertFalse(class_exists('\\'.ExistingClass::class, false));
$this->assertTrue(class_exists('NonExistingClass'));
$this->assertTrue(class_exists('NonExistingClass', false));
$this->assertTrue(class_exists('\\NonExistingClass'));
$this->assertTrue(class_exists('\\NonExistingClass', false));
$this->assertTrue(class_exists(ExistingClassReal::class));
$this->assertTrue(class_exists(ExistingClassReal::class, false));
$this->assertTrue(class_exists('\\'.ExistingClassReal::class));
$this->assertTrue(class_exists('\\'.ExistingClassReal::class, false));
$this->assertFalse(class_exists('NonExistingClassReal'));
$this->assertFalse(class_exists('NonExistingClassReal', false));
$this->assertFalse(class_exists('\\NonExistingClassReal'));
$this->assertFalse(class_exists('\\NonExistingClassReal', false));
}

public function testInterfaceExists()
{
$this->assertFalse(interface_exists(ExistingInterface::class));
$this->assertFalse(interface_exists(ExistingInterface::class, false));
$this->assertFalse(interface_exists('\\'.ExistingInterface::class));
$this->assertFalse(interface_exists('\\'.ExistingInterface::class, false));
$this->assertTrue(interface_exists('NonExistingInterface'));
$this->assertTrue(interface_exists('NonExistingInterface', false));
$this->assertTrue(interface_exists('\\NonExistingInterface'));
$this->assertTrue(interface_exists('\\NonExistingInterface', false));
$this->assertTrue(interface_exists(ExistingInterfaceReal::class));
$this->assertTrue(interface_exists(ExistingInterfaceReal::class, false));
$this->assertTrue(interface_exists('\\'.ExistingInterfaceReal::class));
$this->assertTrue(interface_exists('\\'.ExistingInterfaceReal::class, false));
$this->assertFalse(interface_exists('NonExistingClassReal'));
$this->assertFalse(interface_exists('NonExistingClassReal', false));
$this->assertFalse(interface_exists('\\NonExistingInterfaceReal'));
$this->assertFalse(interface_exists('\\NonExistingInterfaceReal', false));
}

public function testTraitExists()
{
$this->assertFalse(trait_exists(ExistingTrait::class));
$this->assertFalse(trait_exists(ExistingTrait::class, false));
$this->assertFalse(trait_exists('\\'.ExistingTrait::class));
$this->assertFalse(trait_exists('\\'.ExistingTrait::class, false));
$this->assertTrue(trait_exists('NonExistingTrait'));
$this->assertTrue(trait_exists('NonExistingTrait', false));
$this->assertTrue(trait_exists('\\NonExistingTrait'));
$this->assertTrue(trait_exists('\\NonExistingTrait', false));
$this->assertTrue(trait_exists(ExistingTraitReal::class));
$this->assertTrue(trait_exists(ExistingTraitReal::class, false));
$this->assertTrue(trait_exists('\\'.ExistingTraitReal::class));
$this->assertTrue(trait_exists('\\'.ExistingTraitReal::class, false));
$this->assertFalse(trait_exists('NonExistingClassReal'));
$this->assertFalse(trait_exists('NonExistingClassReal', false));
$this->assertFalse(trait_exists('\\NonExistingTraitReal'));
$this->assertFalse(trait_exists('\\NonExistingTraitReal', false));
}
}

class ExistingClass
{
}

class ExistingClassReal
{
}

interface ExistingInterface
{
}

interface ExistingInterfaceReal
{
}

trait ExistingTrait
{
}

trait ExistingTraitReal
{
}

0 comments on commit a9694f7

Please sign in to comment.