I'm currently writing tests for a package in Laravel 4.
I am mocking the Illuminate\Database\Query\Builder
which works nearly all the time except when a where
method uses a callback, I cannot check if the methods inside the callback are called.
I was hoping one of you could shed some light.
$query = \Mockery::mock('Illuminate\Database\Query\Builder', function ($mock) {
/** @var \Mockery\Mock $mock */
$mock->shouldReceive('where');
$mock->shouldReceive('orWhere')->twice();
});
And the actual where
method that should call orWhere
. Note: That builder mock gets passed to the class below.
$builder = new LaravelBuilder($query);
Which then calls $builder->filter()
which contains the below code.
$this->query->where(function ($query) use ($filterData) {
/** @var \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query */
foreach($filterData['columns'] as $colData) {
/** @var \Samvaughton\Ldt\Column $column */
$column = $colData['column'];
// See if this column is searchable
if (!$column->isSearchable() || !$colData['searchable']) continue;
// If the individual column term is empty, use the main term
$term = (empty($colData['term'])) ? $filterData['term'] : $colData['term'];
// Actually apply the filter
$query->orWhere($column->getSqlColumn(), "LIKE", "%{$term}%");
}
});
The main part is the bottom bit $query->orWhere
, the PHPUnit tests fail every time as it doesn't run the orWhere
even once. Before you say that it might not reach the execution of it due to continue
, the data I am passing will allow this.
I suspect this is due to how I am mocking the where
method in the first place. If I include an exit
before the foreach
it does not execute indicating that it doesn't even run anything inside the callback. I know that is the default behaviour but how do I get Mockery to run the same/similar callback?
I have tried using partial mocks, using shouldExpect
but don't fully understand it. I've also tried searching around but no luck with this scenario.
Would be great if I could learn how to use mockery within callbacks.
OK so I think this can be done a better way, but I resorted to partially mocking Laravel's query builder.
See line 324 in Illuminate\Database\Query\Builder
When using a closure Laravel spawns a new query builder which also needs to be mocked. Here's the code that worked:
$query = \Mockery::mock('Illuminate\Database\Query\Builder', function ($mock) {
/** @var \Mockery\Mock $mock */
$mock->makePartial();
$mock->shouldReceive('where')->once()->passthru();
$mock->shouldReceive('newQuery')->andReturn(
\Mockery::mock('Illuminate\Database\Query\Builder', function ($mock) {
/** @var \Mockery\Mock $mock */
$mock->makePartial();
$mock->shouldReceive('orWhere')->twice();
})
);
});
I needed to use makePartial()
so the query builder retained its original functionality for closures (which is not true to unit testing) so this isn't the best solution. From here I mock the newQuery
method which is called to spawn a new query builder instance which I then mock in a similar format.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With