Home > OS >  How to unit test createQueryBuilder
How to unit test createQueryBuilder

Time:05-14

I have this method:

public function findAllBy(?string $categoryId): array
    {
        $qb = $this->entityManager->createQueryBuilder()
            ->select('p')
            ->from(Product::class, 'p');

        if (null !== $categoryId) {
            $qb->from(Category::class, 'cc')
               ->join(CategoryProduct::class, 'cp', Join::WITH, 'p.id = cp.product_id')
               ->join(Category::class, 'c', Join::WITH, 'cp.category_id = c.id')
               ->where('cc.id = :id')
               ->andWhere('cc.left <= c.left')
               ->andWhere('cc.right >= c.right')
               ->setParameter('id', $categoryId, 'uuid');
        }

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

I am trying to test it this way ( obviously not the correct one ):

public function testFindAllProductsByFilters():void
    {
        $entityRepositoryMock = $this->createMock(EntityRepository::class);
        $entityManagerMock = $this->createMock(EntityManagerInterface::class);
        $entityManagerMock->expects($this->once())
            ->method('getRepository')
            ->with(Product::class)
            ->willReturn($entityRepositoryMock);
        $entityManagerMock->expects($this->once())
            ->method('createQueryBuilder')
            ->willReturn($entityRepositoryMock);

        $this->repository = new DoctrineProductRepository($entityManagerMock);
        $this->assertIsArray($this->repository->findAllBy(ProductFactory::CATEGORY_ID_FIRST));
    }

The error I get: 1)

App\Tests\Unit\Infrastructure\Domain\Model\Product\DoctrineProductRepositoryTest::testFindAllProductsByFilters Error: Call to a member function from() on null

Is this piece of code even testable by Unit Test ?

CodePudding user response:

As you should not mock what you don't own, my suggestion is to avoid unit tests in this kind of scenarios. Moreover I would not use (abuse) a mock (a test double in general) as you're testing the implementation and not the behaviour of your SUT.

Let's see an example

class Foo()
{
  public function doSomething(): int
  {
    // some heavy logic here
  }
}

class Bar()
{
  public function doSomething(Foo $foo): int
  {
    $result = $foo->doSomething();
    // other elaboration upon $result
  } 
}

That's a deliberately trivial example of course. If you use a test double in Bar test, you would write something like`

$fooMock->expects($this->once())
  ->method('doSomething')
  ->willReturn(1);

Let's say that Foo changes it's public API, renaming doSomething to doSomethingElse. What happens? You need to change the test aswell even if Foo behaviour didn't changed at all.

As said before it's a trivial example but should give you an idea.

  • Related