如何模拟EntityManager for phpUnit? [英] How to mock EntityManager for phpUnit?

查看:177
本文介绍了如何模拟EntityManager for phpUnit?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a Unit test for this class:

<?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的查询构建器,但是没有实际的数据库连接,我不知道该返回什么。你可以帮助我吗?

And I want to Mock up the Doctrine's entity Manager. But I do not know what to return in order to successfully use the Doctrine's Query Builder but whithout an actual Database Connection. Can you help me?

推荐答案

嗯,如果你真的遵循最佳做法,你不应该模仿实体经理,因为你不拥有它您可以在以下链接中阅读更多信息

Well, if you want really follow the best practices, you should not mock entity manager as you don't own it; you can read more at following links

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

Ok, now, if you want to walk that road, you can mock EntityManager like you mock every other objects in PHPUnit

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

if you work with PHPUnit >= 5.7 and PHP > 5.5

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

或PHP <= 5.5

or for PHP <= 5.5

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

一旦你嘲笑它,你必须声明所有罐装的回应和期望:罐装回复,以便让你的代码工作,而期望为了让它成为一个模拟

Once you mocked it, you have to declare all canned response and expectations: canned response in order to let your code work whereas expectations in order to let it to be a mock

只是为了举个例子,这应该是canned

Just to make an example, this should be canned

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

正如你可以看到的,为每个方法调用声明一个canned方法可能是非常困难和overkill;例如,在这里,你应该这样做

As you can will see, declare a canned method for every method call could be very difficult and overkill; for instance, here, you should act that way

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

(当然你必须用 ... 具有实际值)

(of course you have to substitute ... with real values)

最后,请记住, Mocks不是Stubs

这篇关于如何模拟EntityManager for phpUnit?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆