我正在为这个类编写一个单元测试:
<?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