6
\$\begingroup\$

I am trying to unit test a small class that I consider part of the "business-logic" layer of my project. It mostly just interact's with Doctrine's entity manager and a couple of entities with a many-to-many association. I'm finding that unit testing it while mocking out its dependencies is just ludicrous. Just the code to set up mocks is going to be at least twice as long as the class itself. What am I doing wrong, or should I just not bother doing the mocks and let it interact with a real test database?

Here is the class I am trying to test:

class UserPermissions{

    protected $em;

    public function __construct($entityManager){
        $this->em = $entityManager;
    }

    /**
     * returns user's permissions for all products or specific product
     *
     **/
    public function getFromUUID($uuid, $productId=NULL){

        $user = $this->em->find('\MyProject\Entity\User',$uuid);
        $product = $productId ? $this->em->find('\MyProject\Entity\Product',$productId) : NULL;

        if ($user===null) {
            return null;
        } else {
            $userProducts = $user->getUserProducts();
            $result = [];

            if ($productId) {
                $criteria = Criteria::create()
                    ->where(Criteria::expr()->eq("product",$product))
                    ;
                $userProducts = $userProducts->matching($criteria);
            }

            for ($i = 0; $i < count($userProducts); $i++) {
                $productI = $userProducts[$i];
                array_push($result, array(
                    "product" => $productI->getProduct()->getId(),
                    "permissions" => $productI->getPermissions(),
                ));
            }

            return $result;
        }
    }
}

And here is what my test class is starting to look like:

class UserPermissionsTest extends PHPUnit_Framework_TestCase
{
    protected $testService;
    protected $mockProduct;
    protected $mockUser;
    protected $mockUserProduct;

    protected function setUp(){

        $em = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
                   ->disableOriginalConstructor()
                             ->getMock();

        $emMap = array(
            array('\MyProject\Entity\User','a',$this->mockUser),
            array('\MyProject\Entity\Product','b',$this->mockProduct),
        );

        $em->expects($this->any())
           ->method('find')
             ->will($this->returnValueMap($emMap));


//      $userMockBuilder = $this->getMockBuilder('\MyProject\Entity\User')
//                              ->disableOriginalConstructor()

        $user = $this->getMockBuilder('\MyProject\Entity\User')
                     ->disableOriginalConstructor()
                                 ->getMock();

        $user->expects($this->any())
             ->method('getUserProducts')
                 ->will($this->returnValue($this->mockUserProduct);

        $this->mockUser=$user;

        $userProduct = $this->getMockBuilder('\MyProject\Entity\UserProduct')
                       ->disableOriginalConstructor()
                                   ->getMock();

        $userProduct->expects($this->any())
                    ->method('matching')
                        ->will($this->returnValue($this->mockProduct);

        $this->mockUserProduct = $userProduct;

        $product =$this->getMockBuilder('\MyProject\Entity\Product')
                     ->disableOriginalConstructor()
                                 ->getMock();

        $product->expects($this->any())
                    ->method('getProduct')
                        ->will($this->returnValue($this->mockProduct);

.... and so on. It's obviously not done yet.

\$\endgroup\$
3
\$\begingroup\$

I decided that there was too much database access code in my UserPermissions class and split it into two classes. The first class is now much easier to mock and the second class is just a wrapper for a doctrine query, which is probably almost pointless to mock.

class UserPermissions{

    protected $dbalUserProducts;

    public function __construct($dbalUserProducts){
        $this->dbalUserProducts= $dbalUserProducts;
    }

    /**
     * returns user's permissions for all products or specific product
     **/
    public function get($userId, $productId=NULL){

        $userProducts = $this->dbalUserProducts->get($userId, $productId);

        $result = [];

        for ($i = 0; $i < count($userProducts); $i++) {
            $up = $userProducts[$i];
            array_push($result, array(
                "product" => $up->getProduct()->getId(),
                "permissions" => $up->getPermissions(),
            ));
        }

        return $result;
    }
}

class UserProductQuery
{
    protected $em;

    public function __construct($entityManager)
    {
        $this->em = $entityManager;
    }

    public function get($userId=null, $productId=null){
        $qb = $this->em->createQueryBuilder();

        $qb->select('u')
           ->from('\MyProject\Entity\UserProduct', 'u');

        if ($userId) {
            $qb->andWhere($qb->expr()->eq('u.user', ':userId'));
            $qb->setParameter('userId',$userId);
        }
        if ($productId) {
            $qb->andWhere($qb->expr()->eq('u.product', ':productId'));
            $qb->setParameter('productId',$productId);
        }

        return($qb->getQuery()->getResult());
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.