Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question
up vote 3 down vote accepted

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());
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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