如何模拟phpUnit的EntityManager



我正在为这个类编写一个单元测试:

<?php
namespace AppBundleManagersCRUD;
use DoctrineORMEntityManager;
use CommonLibsInterfacesCrudManagerInterface;
use AppBundleEntityPet;
use CommonLibsHelpersPaginatorHelper;
use AppBundleEntityPerson;
use AppBundleManagersCRUDPetManager;
use AppBundleAppBundle;
class PersonManager extends CrudManagerInterface
{
/**
* @var EntityManager
*/
private $em;
/**
* @var PetManager
*/
private $petManager;
public function __construct(EntityManager $em,PetManager $petManager)
{
$this->em=$em;
$this->petManager=$petManager;
}
/**
* {@inheritDoc}
* @see CommonLibsInterfacesCrudManagerInterface::search()
* @return AppBundleEntityPerson[]
*/
public function search(array $searchParams, array $order, $page, $limit)
{
$queryBuilder=$this->em->createQueryBuilder();
$queryBuilder=$queryBuilder->select('p')->from('AppBundle:Person','p');
if(isset($searchParams[Person::NAME])) {
$queryBuilder->andWhere('p.name LIKE :name')->setParameter('name','%'.$searchParams[Person::NAME].'%');
}
$petNameSearch=isset($searchParams[Pet::NAME]);
$petTypeSearch=isset($searchParams[Pet::TYPE]);
if( $petNameSearch || $petTypeSearch ) {
$queryBuilder->join('p.pets','pe');
if($petNameSearch) {
$queryBuilder->andWhere('pe.name LIKE :pet_name')->setParameter('pet_name','%'.$searchParams[Pet::NAME].'$');
}
if($petTypeSearch) {
if(!is_array($searchParams[Pet::TYPE])) {
$searchParams[Pet::TYPE]=array($searchParams[Pet::TYPE]);
}
$queryBuilder->andWhere('pe.type IN (:pet_types)')->setParameter('pet_types',$searchParams[Pet::TYPE]);
}
/**
* @var DoctrineORMQuery
*/
$query=$queryBuilder->getQuery();
if((int)$limit>0) {
$query->setFirstResult(PaginatorHelper::calculateFirstResult($page,$limit))->setMaxResults((int)$limit);
}
$results=$query->getResult();
return $results;
}
}
/**
* {@inheritDoc}
* @see CommonLibsInterfacesCrudManagerInterface::getById()
* @return AppBundleEntityPerson
*/
public function getById($id)
{
return $this->em->getManager('AppBundle:Person')->findById($id);
}

/**
* {@inheritDoc}
* @see CommonLibsInterfacesCrudManagerInterface::add()
* 
* @param array $dataToAdd 
* 
* $dataToAdd Must have one of the follofiwng formats:
* 
* FORMAT 1:
* [
*  Person:NAME=>"value"
* ]
* 
* FORMAT 2:
* 
* [
*  [
*      Person:NAME=>"value"
*  ],
*  [
*      Person:NAME=>"value"
*  ],
*  [
*      Person:NAME=>"value"
*  ]
* ]
* 
* @return AppBundleEntiryPerson[] with the modified persons
*/
public function add(array $dataToAdd)
{       
/**
* @var AppBundleEntiryPerson $insertedPersons
*/
$insertedPersons=[];
foreach($dataToAdd as $key=>$data) {
$personToInsert=new Person();
if(is_array($data)) {   
$personToInsert=$this->add($data);
if($personToInsert==false) {
return false;
}
} elseif(!$this->setReference($personToInsert,$key,$data)) {
$personToInsert->$$key=$data;
}
if(is_array($personToInsert)) {
$insertedPersons=array_merge($insertedPersons,$personToInsert);
} else {
$this->em->flush($personToInsert);
$insertedPersons[]=$personToInsert;
}
}
if(!empty($insertedPersons)) {
$this->em->flush();
}
return $insertedPersons;
}
/**
* {@inheritDoc}
* @see CommonLibsInterfacesCrudManagerInterface::edit()
*/
public function edit(array $changedData)
{
$em=$this->em->getManager('AppBundle:Person');
foreach($changedData as $id => $fieldsToChange) {
$item=$this->getById($id);
foreach($fieldsToChange as $fieldName=>$fieldValue){
if(!$this->setReference($item,$fieldName,$fieldValue)){             
$item->$$fieldName=$fieldValue;
}
}
$em->merge($item);
}
$em->flush();
}
/**
* {@inheritDoc}
* @see CommonLibsInterfacesCrudManagerInterface::delete()
* 
* @param array changedData
* Should contain data in the following formats:
* FORMAT 1:
* 
* [
*  Person::ID=>^an_id^
*  Person::NAME=>^a name of a person^
* ]
* 
* FORMAT2:
* [
*  Person::ID=>[^an_id1^,^an_id2^,^an_id3^...,^an_idn^]
*  Person::NAME=>^a name of a person^
* ]
* 
* The $changedData MUST contain at least one of Person::ID or Person::NAME.
* Therefore you can ommit one of Person::ID or Person::NAME but NOT both.
*/
public function delete(array $changedData)
{
$queryBuilder=$this->em->createQueryBuilder();
$queryBuilder->delete()->from('AppBundle:Person','p');
$canDelete=false;
if(isset($changedData[Person::ID])) {
if(!is_array($changedData[Person::ID])) {
$changedData[Person::ID]=[$changedData[Person::ID]];
}
$queryBuilder->where('person.id IN (:id)')->bindParam('id',$changedData[Person::ID]);
$canDelete=true;
}
if(isset($changedData[Person::NAME])) {
$queryBuilder->orWhere('person.name is :name')->bindParam('name',$changedData[Person::NAME]);
$canDelete=true;
}
if($canDelete) {
$query=$queryBuilder->getQuery();
$query->execute();  
}
return $canDelete;
}
/**
* Set referencing fields to person.
* 
* @param AppBundleEntiryPerson $item The item to set the reference
* @param string $referencingKey A string that Indicates the input field.
*  The strings for the param above are defined as constants at AppBundleEntiryPerson.
* @param mixed $referencingValue The value of referencing key
* 
* @return boolean
*/
private function setReference($item,$referencingKey,$referencingValue)
{
/**
* @var AppBundleEntityPet $pet
*/
$pet=null;
if($referencingKey===Person::PET) {
if(is_numeric($referencingValue)) {//Given pet id
$pet=$this->petManager->getById($referencingValue);//Searching pet by id
} elseif (is_object($referencingValue) 
&& $referencingValue instanceof AppBundleEntityPet 
){//Given directly a pet Object
$pet=$referencingValue;
}
$item->$$referencingKey=$referencingValue;
return true;
}
return false;
}
}

我想模仿条令的实体经理。但我不知道该返回什么才能成功使用Doctrine的查询生成器,但没有实际的数据库连接。

好吧,如果你想真正遵循最佳实践,你不应该嘲笑实体管理器,因为你不拥有它;你可以在以下链接阅读更多

  • https://github.com/mockito/mockito/wiki/How-to-write-good-tests

  • https://adamwathan.me/2017/01/02/dont-mock-what-you-dont-own/

  • https://8thlight.com/blog/eric-smith/2011/10/27/thats-not-yours.html

好的,现在,如果你想走那条路,你可以像模拟PHPUnit中的其他对象一样模拟EntityManager

如果您使用PHPUnit>=5.7和PHP>5.5

$mockedEm = $this->createMock(EntityManager::class)

或者对于PHP<=5.5

$mockedEm = $this->createMock('Doctrine\ORM\EntityManager');

一旦你嘲笑了它,你就必须声明所有的屏蔽响应和期望:屏蔽响应是为了让你的代码工作,而期望是为了让它成为一个模拟

举个例子,这应该是罐装

return $this->em->getManager('AppBundle:Person')->findById($id);

正如您将看到的,为每个方法调用声明一个罐装方法可能非常困难,而且过于繁琐;例如,在这里,你应该这样做

$mockedEm = $this->createMock(EntityManager::class)
$mockedPersonManager = $this->createMock(...);
$mockedEm->method('getManager')->willReturn(mockedPersonManager);
$mockedPersonManager->findOneBy(...)->willReturn(...);

(当然,你必须用实际值代替...)

最后,记住Mocks不是Stubs

相关内容

  • 没有找到相关文章

最新更新