我正在为这堂课写一个单元测试:
<?php
namespace AppBundle\Managers\CRUD;
use Doctrine\ORM\EntityManager;
use CommonLibs\Interfaces\CrudManagerInterface;
use AppBundle\Entity\Pet;
use CommonLibs\Helpers\PaginatorHelper;
use AppBundle\Entity\Person;
use AppBundle\Managers\CRUD\PetManager;
use AppBundle\AppBundle;
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 \CommonLibs\Interfaces\CrudManagerInterface::search()
* @return AppBundle\Entity\Person[]
*/
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 Doctrine\ORM\Query
*/
$query=$queryBuilder->getQuery();
if((int)$limit>0) {
$query->setFirstResult(PaginatorHelper::calculateFirstResult($page,$limit))->setMaxResults((int)$limit);
}
$results=$query->getResult();
return $results;
}
}
/**
* {@inheritDoc}
* @see \CommonLibs\Interfaces\CrudManagerInterface::getById()
* @return AppBundle\Entity\Person
*/
public function getById($id)
{
return $this->em->getManager('AppBundle:Person')->findById($id);
}
/**
* {@inheritDoc}
* @see \CommonLibs\Interfaces\CrudManagerInterface::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 AppBundle\Entiry\Person[] with the modified persons
*/
public function add(array $dataToAdd)
{
/**
* @var AppBundle\Entiry\Person $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 \CommonLibs\Interfaces\CrudManagerInterface::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 \CommonLibs\Interfaces\CrudManagerInterface::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 AppBundle\Entiry\Person $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 AppBundle\Entiry\Person.
* @param mixed $referencingValue The value of referencing key
*
* @return boolean
*/
private function setReference($item,$referencingKey,$referencingValue)
{
/**
* @var AppBundle\Entity\Pet $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 AppBundle\Entity\Pet
){//Given directly a pet Object
$pet=$referencingValue;
}
$item->$$referencingKey=$referencingValue;
return true;
}
return false;
}
}
我想要嘲笑Doctrine的实体经理。但是我不知道要返回什么才能成功使用Doctrine的查询生成器但没有实际的数据库连接。
答案 0 :(得分:3)
好吧,如果你真的想要遵循最佳实践,你不应该嘲笑实体经理,因为你不拥有它;您可以在以下链接中阅读更多内容
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
好的,现在,如果你想走这条路,你可以模仿EntityManager
,就像模仿PHPUnit
如果你使用PHPUnit&gt; = 5.7和PHP&gt; 5.5
$mockedEm = $this->createMock(EntityManager::class)
或PHP&lt; = 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 are not Stubs