如何为phpUnit模拟EntityManager?

时间:2017-02-11 12:48:09

标签: unit-testing symfony doctrine-orm phpunit

我正在为这堂课写一个单元测试:

<?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的查询生成器但没有实际的数据库连接。

1 个答案:

答案 0 :(得分:3)

好吧,如果你真的想要遵循最佳实践,你不应该嘲笑实体经理,因为你不拥有它;您可以在以下链接中阅读更多内容

好的,现在,如果你想走这条路,你可以模仿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