2017-02-11 13 views
0

私はこのクラスのユニットテストを書いています:phpUnit用にEntityManagerをモックする方法は?

<?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のQuery Builderを正常に使用するためには何を返すべきかわからないのですが、実際のデータベース接続はありません。手伝って頂けますか?

答えて

2

実際にベストプラクティスに従ってほしい場合は、エンティティマネージャーを所有していないので、エンティティマネージャーを模倣すべきではありません。あなたはその道を歩くしたい場合は[OK]を、今、あなたはEntityManagerを模擬することができ、リンク

を以下で詳細を読むことができますあなたが他のすべてのオブジェクトを模倣しているようにPHPUnit

あなたは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 are not Stubs

+0

は、だから私のようにエンティティマネージャを模擬していないことを覚えておいてくださいベストプラクティスですが、別のクラスに注入された場合にPersonManager自体をモックします。 –

+0

@DimitriosDesyllasまあ、私はそれが最高のベストプラクティスではないと言いました。単体テストが本当に必要な場合は、模擬/スタブすることができます。それ以外の場合、統合テストを行うことができれば、ユニットテスト(この場合) – DonCallisto

関連する問題