简体   繁体   中英

Mocking callbacks in Laravel 4 (Mockery)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM