harryg
harryg

Reputation: 24107

In Laravel, mocking an eloquent query on a model

I'm stuck on Laravel 4.2 for now (with phpunit and mockery) but the same should apply to later versions.

I have a Repository for my FxRate model. It has a method to get an FX rate vs GBP which contains this eloquent call:

$query = \FxRate::where('currency', $currency )
    ->where('fx_date', $fxDate->format('Y-m-d') )
    ->first();

return $query->rate_to_gbp;

In my unit test I'd like to mock this call so I can define the query result that would be returned by this call rather than relying on the database to have a value within it.

My attempt goes something like this:

$mocked_query_result = (object) ['rate_to_gbp' => 1.5];

FxRate::shouldReceive('where')
       ->once()
       ->andReturn($mocked_query_result);

But I'm fairly sure this won't work as the initial static call to FxRate should return some query object that accepts a further where() call and a first().

Is there a clean way of mocking this?

Upvotes: 5

Views: 10031

Answers (1)

markdwhite
markdwhite

Reputation: 2449

You should pass an instance of your model into the repository in the constructor:

public function __construct(FXRate $model) 
{
    $this->model = $model;
}

Then your query becomes:

$query = $this->model->where('currency', $currency)...etc

Then you pass a mocked model to the repo when you instantiate it:

$mockModel = Mockery::mock('FXRate');
// This could be better, and you should use correct with() calls but hey, it's only an example
$mockModel->shouldReceive('where')
    ->twice()
    ->andReturn($mockModel);
$mockModel->shouldReceive('first')
    ->once()
    ->andReturn($mocked_query_result);
$repo = new Repo($mockModel)
$this->assertEquals($mocked_query_result, $repo->testableMethod());

Further edit following comments. You can return a mock of any model but I find mocking the real model helps with readability:

$mockFXRate = Mockery::mock('FXRate');
$mockFXRate->shouldReceive('where')
    ->once()
    ->andReturn($mockFXRate);
$mockFXRate->shouldReceive('first')
    ->once()
    ->andReturn($mocked_query_result);
FXRate::shouldReceive('where')
    ->andReturn($mockFXRate);

Upvotes: 3

Related Questions