Last active
July 27, 2018 13:16
-
-
Save jakzal/cecbd34ecd88367c8b37b33613ba8abe to your computer and use it in GitHub Desktop.
[Idea] Automated injection of test doubles into phpunit test cases
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Zalas\Injector\Tests\Service; | |
use PHPUnit\Framework\TestCase; | |
use Prophecy\Prophecy\ObjectProphecy; | |
use Psr\Container\ContainerInterface; | |
use Psr\Container\NotFoundExceptionInterface; | |
use Zalas\Injector\PHPUnit\TestCase\ServiceContainerTestCase; | |
use Zalas\Injector\PHPUnit\TestListener\ServiceInjectorListener; | |
use Zalas\Injector\Service\ContainerFactory; | |
class MockerTest extends TestCase implements ServiceContainerTestCase | |
{ | |
/** | |
* @var ContainerFactory|ContainerInterface|ObjectProphecy | |
* @mock | |
* @inject | |
*/ | |
private $containerFactory; | |
/** | |
* @var ContainerInterface|ObjectProphecy | |
* @mock | |
* @inject | |
*/ | |
private $container; | |
public function test_it_injects_test_doubles() | |
{ | |
$listener = new ServiceInjectorListener(); | |
$listener->startTest($this); | |
$this->assertInstanceOf(ObjectProphecy::class, $this->containerFactory); | |
$this->assertInstanceOf(ObjectProphecy::class, $this->container); | |
$this->assertInstanceOf(ContainerFactory::class, $this->containerFactory->reveal()); | |
$this->assertInstanceOf(ContainerInterface::class, $this->containerFactory->reveal()); | |
$this->assertInstanceOf(ContainerInterface::class, $this->container->reveal()); | |
} | |
public function test_it_verifies_test_double_expectations() | |
{ | |
$listener = new ServiceInjectorListener(); | |
$listener->startTest($this); | |
$this->containerFactory->create()->willReturn($this->container); | |
$this->container->reveal()->has('foo'); | |
$this->container->has('foo')->shouldHaveBeenCalled(); | |
} | |
/** | |
* @return ContainerInterface | |
*/ | |
public function createContainer(): ContainerInterface | |
{ | |
$testDoubler = function ($type): ObjectProphecy { | |
return $this->prophesize($type); | |
}; | |
return new class($testDoubler) implements ContainerInterface { | |
/** | |
* @var callable | |
*/ | |
private $testDoubler; | |
public function __construct(callable $testDoubler) | |
{ | |
$this->testDoubler = $testDoubler; | |
} | |
public function get($id) | |
{ | |
if (!$this->has($id)) { | |
throw new class extends \Exception implements NotFoundExceptionInterface { | |
}; | |
} | |
$ids = array_filter($this->splitIds($id), function ($id) { | |
return ObjectProphecy::class !== $id; | |
}); | |
$prophecy = call_user_func($this->testDoubler, array_shift($ids)); | |
foreach ($ids as $id) { | |
if (interface_exists($id)) { | |
$prophecy->willImplement($id); | |
} else { | |
$prophecy->willExtend($id); | |
} | |
} | |
return $prophecy; | |
} | |
public function has($id) | |
{ | |
return in_array(ObjectProphecy::class, $this->splitIds($id)); | |
} | |
private function splitIds($id): array | |
{ | |
return array_map(function ($id) { | |
return ltrim($id, '\\'); | |
}, explode('|', $id)); | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The above idea built on top of
zalas/phpunit-injector
which was created with a different use case in mind.A slightly different (independent) approach has been implemented in
zalas/phpunit-doubles
here: https://packagist.org/packages/zalas/phpunit-doubles