-
-
Save Rho-bur/f8a01246b7ed3f49826f36640b3ec3e6 to your computer and use it in GitHub Desktop.
Mock some services for PHPUnit
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 | |
namespace Traits\Tests; | |
/** | |
* Mocks the entity manager | |
* | |
* Provides everything in the memory, so the tests does not depend on doctrine, | |
* which does a lot of stuff (maybe too much). This also allows to avoid to | |
* need and modify the data in the database, even if those are for the tests. | |
* | |
* This mock provides the entity manager, a way to persist objets in memory | |
* (and also increment an id if it can do it through a setId method), and also | |
* manages repositories. | |
*/ | |
trait MockEntityManager | |
{ | |
use MockWithoutConstructor; | |
private $entityManager = ['init' => false, | |
'mocks' => [], | |
'repos' => [], | |
'entities' => []]; | |
/** | |
* Initializes the mocked entity manager | |
*/ | |
public function initEntityManager() | |
{ | |
if ($this->entityManager['init']) { | |
return; | |
} | |
$this->entityManager['mocks'] = ['em' => $this->getMockWithoutConstructor('\Doctrine\ORM\EntityManager'), | |
'repos' => []]; | |
$this->entityManager['mocks']['em']->expects($this->any()) | |
->method('getRepository') | |
->will($this->returnCallback(function ($repo) { | |
return $this->getRepository($repo); | |
})); | |
$callbackEntity = function ($method = 'persist') { | |
return function ($entity) use ($method) { | |
// don't even bother if it's not an object | |
if (!is_object($entity)) { | |
return; | |
} | |
$alias = explode('\\', get_class($entity)); | |
$alias = lcfirst(array_pop($alias)); | |
if (!isset($this->entityManager['entities'][$alias])) { | |
$this->entityManager['entities'][$alias] = ['sequence' => 0, | |
'objects' => []]; | |
} | |
$found = null !== $entity->getId() | |
? array_search($this->getEntityByAliasAndId($alias, $entity->getId()), $this->entityManager['entities'][$alias]['objects']) | |
: false; | |
switch ($method) { | |
case 'persist': | |
if (false === $found) { | |
$found = $this->entityManager['entities'][$alias]['sequence']++; | |
if (method_exists($entity, 'setId')) { | |
$entity->setId($this->entityManager['entities'][$alias]['sequence']); | |
} | |
} | |
$this->entityManager['entities'][$alias]['objects'][$found] = $entity; | |
break; | |
case 'remove': | |
if (false !== $found) { | |
unset($this->entityManager['entities'][$alias]['objects'][$found]); | |
} | |
break; | |
} | |
}; | |
}; | |
$this->entityManager['mocks']['em']->expects($this->any()) | |
->method('persist') | |
->will($this->returnCallback($callbackEntity('persist'))); | |
$this->entityManager['mocks']['em']->expects($this->any()) | |
->method('flush') | |
->will($this->returnValue(null)); | |
$this->entityManager['mocks']['em']->expects($this->any()) | |
->method('remove') | |
->will($this->returnCallback($callbackEntity('remove'))); | |
$this->entityManager['init'] = true; | |
} | |
/** | |
* Get the mocked entity manager. | |
* | |
* Initializes it if it was not already initialized. | |
*/ | |
public function getMockedEntityManager() | |
{ | |
$this->initEntityManager(); | |
return $this->entityManager['mocks']['em']; | |
} | |
/** | |
* Add a repository to be mocked. | |
* | |
* It also registers an alias for this repo | |
* | |
* @param string $alias Alias for this repository | |
* @param string $fqcn Class name of the repository | |
* @param array $options Options to be given to this repository. Not used yet. | |
*/ | |
public function addRepository($alias, $fqcn, array $options = []) | |
{ | |
$this->entityManager['mocks']['repos'][$alias] = $this->getMockWithoutConstructor($fqcn); | |
$this->entityManager['repos'][$alias] = ['fqcn' => $fqcn, | |
'options' => $options]; | |
} | |
/** | |
* Get a repository. | |
* | |
* Searches first in the alias, then with the repo's classname | |
* | |
* @param string $repoName repo to get | |
* @return null|object null if not found, the mock otherwise | |
*/ | |
public function getRepository($repoName) | |
{ | |
// first try to load via an alias | |
$repo = $this->getRepositoryByAlias($repoName); | |
if (null === $repo) { | |
$repo = $this->getRepositoryByClass($repoName); | |
} | |
return $repo; | |
} | |
/** | |
* Gets a repository by its alias. | |
* | |
* @param string $alias Alias to search | |
* @return null|object null if not found, the mock otherwise | |
*/ | |
public function getRepositoryByAlias($alias) | |
{ | |
if (!isset($this->entityManager['mocks']['repos'][$alias])) { | |
return null; | |
} | |
return $this->entityManager['mocks']['repos'][$alias]; | |
} | |
/** | |
* Gets a repository by its full classified class name. | |
* | |
* @param string $fqcn Class to search | |
* @return null|object null if not found, the mock otherwise | |
*/ | |
public function getRepositoryByClass($fqcn) | |
{ | |
$found = false; | |
foreach ($this->entityManager['repos'] as $alias => $repo) { | |
if ($fqcn === $repo['fqcn']) { | |
return $this->getRepositoryByAlias($alias); | |
} | |
} | |
return null; | |
} | |
/** | |
* Gets an entity by its alias and its id. | |
* | |
* @param string $alias name of the entity, without the namespaces | |
* @param mixed $id Id to fetch. "null" to get the last entity | |
* @return object|null the entity if found, null otherwise | |
*/ | |
public function getEntityByAliasAndId($alias, $id = null) | |
{ | |
if (!isset($this->entityManager['entities'][$alias])) { | |
return null; | |
} | |
if (0 === count($this->entityManager['entities'][$alias]['objects'])) { | |
return null; | |
} | |
if (null === $id) { | |
$entity = end($this->entityManager['entities'][$alias]['objects']); | |
reset($this->entityManager['entities'][$alias]['objects']); | |
return $entity; | |
} | |
foreach ($this->entityManager['entities'][$alias]['objects'] as $oid => $entity) { | |
if ($id === ($oid + 1) || $id === $entity->getId()) { | |
return $entity; | |
} | |
} | |
return null; | |
} | |
/** | |
* Gets the latest entity registered for $alias. | |
* | |
* @param string $alias Name of the entity, without the namespaces | |
* @return object|null the last entity if there is one, null otherwise | |
*/ | |
public function getEntityByAlias($alias) | |
{ | |
return $this->getEntityByAliasAndId($alias, null); | |
} | |
/** | |
* Gets information on all the entities. | |
* | |
* @return array for each entities, return the fqcn, the current sequence | |
* and the number of items | |
*/ | |
public function getAllEntities() | |
{ | |
$tab = []; | |
foreach($this->entityManager['entities'] as $alias => $entities){ | |
if (0 < count($entities['objects'])) { | |
$tab[$alias] = ['entity' => get_class($entities['objects'][0]), | |
'sequence' => $entities['sequence'], | |
'count' => count($entities['objects'])]; | |
} | |
} | |
return $tab; | |
} | |
/** | |
* Gets the entities for a given alias, or all of them if null. | |
* | |
* The difference with getAllEntities() is that this method returns | |
* the object, instead of the information | |
* | |
* @return object[] ALLUVEM ! | |
*/ | |
public function getEntities($alias = null) | |
{ | |
if (null === $alias) { | |
$entities = []; | |
foreach ($this->entityManager['entities'] as $alias) { | |
$entities += $alias['objects']; | |
} | |
return $entities; | |
} | |
if (!isset($this->entityManager['entities'][$alias])) { | |
return null; | |
} | |
return $this->entityManager['entities'][$alias]['objects']; | |
} | |
} |
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 | |
namespace Traits\Tests; | |
use \Symfony\Component\Security\Core\Authentication\Token\TokenInterface, | |
\Symfony\Component\Security\Core\Authentication\SecurityContextInterface; | |
trait MockSecurityContext | |
{ | |
use MockWithoutConstructor; | |
private $securityContext = ['init' => false]; | |
public function initSecurityContext() | |
{ | |
if (true === $this->securityContext['init']) { | |
return; | |
} | |
$this->securityContext = ['mocks' => [], | |
'init' => true]; | |
$this->securityContext['mocks'] = ['context' => $this->getMockWithoutConstructor('\Symfony\Component\Security\Core\SecurityContextInterface'), | |
'token' => $this->getMockWithoutConstructor('\Symfony\Component\Security\Core\Token\TokenInterface')]; | |
$this->securityContext['mocks']['context']->expects($this->any()) | |
->method('setToken') | |
->will($this->returnCallback([$this, 'setToken'])); | |
$this->securityContext['mocks']['context']->expects($this->any()) | |
->method('getToken') | |
->will($this->returnCallback([$this, 'getToken'])); | |
$this->securityContext['mocks']['context']->expects($this->any()) | |
->method('isGranted') | |
->will($this->returnValue(true)); | |
} | |
/** @return SecurityContext */ | |
public function getMockedSecurityContext() | |
{ | |
$this->initSecurityContext(); | |
return $this->securityContext['mocks']['context']; | |
} | |
public function getToken() | |
{ | |
$this->initSecurityContext(); | |
return $this->securityContext['mocks']['token']; | |
} | |
public function setToken(TokenInterface $token) | |
{ | |
$this->initSecurityContext(); | |
$this->securityContext['mocks']['token'] = $token; | |
return; | |
} | |
} |
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 | |
namespace Traits\Tests; | |
trait MockWithoutConstructor | |
{ | |
public function getMockWithoutConstructor($class) | |
{ | |
return $this->getMockBuilder($class)->disableOriginalConstructor()->getMock(); | |
} | |
abstract public function getMockBuilder($class); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment