3
votes

I have a custom entity repository. For example, it looks like this:

namespace Foo\Repository;

use Doctrine\ORM\EntityRepository;

class Article extends EntityRepository
{
    public function findRecent($limit)
    {
        $qb = $this->createQueryBuilder('a');
        $qb->andWhere('a.publishDate IS NOT NULL')
           ->orderBy('a.publishDate', 'DESC')
           ->setMaxResults($limit);

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

I want to test in this case:

  1. There is an ORDER BY in "recent"
  2. There is a limit
  3. The entity must have a publish date

I do not want to validate the SQL output of the query builder, since Doctrine can change the SQL between different versions. That will break my unit test. Therefore, my idea was this:

  1. Create a mock of my repository
  2. Create a mock of the query builder
  3. Make sure $this->createQueryBuilder('a') returns the mocked query builder
  4. Test for method calls on the query builder

In code:

namespace FooTest\Repository;

use PHPUnit_Framework_TestCase as TestCase;

class ArticleRepositoryTest extends TestCase
{
    protected $qb;
    protected $repository;

    public function setUp()
    {
        $this->qb         = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
                                 ->disableOriginalConstructor()
                                 ->getMock();
        $this->repository = $this->getMockBuilder('Foo\Repository\Article')
                                 ->disableOriginalConstructor()
                                 ->getMock();

        $this->repository->expects($this->once())
                         ->method('createQueryBuilder')
                         ->with($this->equalTo('a'))
                         ->will($this->returnValue($this->qb));
    }

    public function testFindRecentLimitsToGivenLimit()
    {
        $limit = '1';
        $this->qb->expects($this->any())
                 ->method('setMaxResults')
                 ->with($this->equalTo($limit));

        $this->repository->findRecent($limit);
    }

    public function testFindRecentOrdersByPublishDate()
    {
        $this->qb->expects($this->any())
                 ->method('andWhere')
                 ->with($this->equalTo('a.publishDate'), $this->equalTo('DESC'));

        $this->repository->findRecent(1);
    }
}

This findRecent() call however never calls createQueryBuilder internally. PhpUnit points out:

FooTest\Repository\ArticleRepositoryTest::testFindRecentLimitsToGivenLimit Expectation failed for method name is equal to when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times.

I think I did something wrong in creating the repository mock. How can I make sure this approach works? Or if there is a better alternative, what is that?

2

2 Answers

0
votes

It looks to me like you are mocking the Repository you are trying to test, so findRecent() is indeed mocked and will return null.

You should be able to use the real repository instance.

0
votes

The solution I found to testing subclassed repositories is to add a call to setMethodsExcept() when building the mock.

So you would modify your code within setUp() above like so:

$this->repository = $this->getMockBuilder('Foo\Repository\Article')
    ->disableOriginalConstructor()
    ->setMethodsExcept([
        // Insert any overridden/implemented functions here, in your case:
        'findRecent',
    ])
    ->getMock();