0

I'm looking for a way to make assertions on the definition of a Criteria in a unit-test (with Phpunit).

I couldn't find any method allowing to access the definition of the Criteria object, thus I've got no way of asserting anything on it.

I'm currently writing unit-tests for a method that creates a Criteria and passes it to a repo :

$criteria = new Criteria();
$criteria->where(
    Criteria::expr()?->andX(
        Criteria::expr()?->eq('isTemporaryFile', 1),
        Criteria::expr()?->orX(
            Criteria::expr()?->lt('creationDate', $maxDate),
            Criteria::expr()?->isNull('creationDate')
        )
    )
);

return $this->myFileRepository->matching($criteria);

In my test, I mocked the repository and I add an expectation for a call to the 'matching' method, with a Criteria as the only parameter :

$this->fileRepository
    ->expects(self::once())
    ->method('matching')
    ->with(
        self::callback(static function (Criteria $c) {
            return $c instanceof Criteria;
        })
    )->willReturn($files);

But I can't find a way to test the criteria itself in the callback.

What I would like to do is something like this :

self::callback(static function (Criteria $c) {
    return $c instanceof Criteria &&
        $c->getSql() === 'isTemporaryFile=1 AND (creationDate<2022-01-01T12:00:00+00:00 OR creationDate IS NULL)';
})

Or something like :

self::callback(static function (Criteria $c) {
    return $c instanceof Criteria &&
        $c->getExpressions()[0]->getField() === 'isTemporaryFile' &&
        $c->getExpressions()[0]->getOp() === '=' &&
        $c->getExpressions()[0]->getValue() === '1' &&
        (...etc)
        ;
})

I tried to get the criteria where expression with $c->getWhereExpression(), but didn't find any way to test it either.

I thought to use the EntityManager to create a Query and retrieve its SQL, but dependency injection doesn't work with tests, and the solution I found seemed a little too heavy. If possible, I'd prefer not having to boot a kernel and instantiate the EntityManager to test a simple Criteria.

I'm using :

  • Symfony 5.4
  • Doctrine 2.9
  • Php 8.0
olinox14
  • 6,177
  • 2
  • 22
  • 39
  • 1
    You can apply criteria to collections. Have you tried creating an ArrayCollection with example data-entries and then assert, that filtering out unwanted entries works? That way you test the actual filtering instead of just, that a certain WHERE class is created. Otherwise, I imagine you will need a booted kernel and a database connection to actually apply the filter to a query and then check the SQL is valid and returns the right results, which it sounds like you don’t want to do. – dbrumann Sep 10 '22 at 06:23
  • @dbrumann Using the criteria directly on the ArrayCollection was an excellent idea, I just tested it and it works nice, thanks! Could you post your comment as an answer so that I mark it as accepted? – olinox14 Sep 12 '22 at 09:53

1 Answers1

0

Thanks to @dbrumann, I managed to solve this by applying the Criteria directly on a Collection, and by testing the result. Here is an example with a simplified criteria that would filter temporary files :

$file1 = $this->getMockBuilder(File::class)->getMock();
$file1->method('getIsTemporaryFile')->willReturn(false);

$file2 = $this->getMockBuilder(File::class)->getMock();
$file1->method('getIsTemporaryFile')->willReturn(true);

$files = new ArrayCollection([$file1, $file2]);

$this->fileRepository
        ->expects(self::once())
        ->method('matching')
        ->with(
            self::callback(static function (Criteria $c) use ($files) {
                $matching = $files->matching($c)->toArray();
                return count($matching) === 1 && array_values($matching)[0] === $files[1];
            })
        )->willReturn($files);
olinox14
  • 6,177
  • 2
  • 22
  • 39