Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockery to return new object with andReturn

I have the following method:

public function getThreads()
{
    $results = \DB::select(\DB::raw(config('sql.messages.threads')), [$this->user->user_id]);

    $messagesThreadsModel = app()->make(MessagesThreadsModel::class);
    $messages = [];

    foreach ($results as $r) {
        $message = $messagesThreadsModel->find($r->messages_thread_id);
        $message->unread = $r->unread;
        $messages[] = $message;
    }

    return $messages;
}

To test the above I mock the call \DB::select (via Laravel's facade) to return the list of objects as would the DB class normally do. Then I load message thread model which is once again mocked and replaced in the container (therefore app()->make() will return its mocked instance, not the actual model).

Finally this:

$messagesThreadsModel->find($r->messages_thread_id);

Is once again mocked to return a dummy object (a stub?). All of it as follows:

$threadsList = $this->mockThreads();

    // mock the raw expression, check the query 
    \DB::shouldReceive('raw')->once()->with(m::on(function($sql)
    {
        return strpos($sql, 'messages') !== false;
    }))->andReturn(true);

    // mock the DB call, return a list of objects
    \DB::shouldReceive('select')->once()->with(true, [$this->usersModel->user_id, $this->usersModel->user_id, $this->usersModel->user_id])->andReturn($threadsList);

    $mockThreadResult = new \StdClass;
    $mockThreadResult->last = "date";

    $this->messagesThreadModel->makePartial();

    // HERE IS THE TRICKY PART!
    $this->messagesThreadModel->shouldReceive('find')->times(count($threadsList))->andReturn($mockThreadResult);

    $this->app->instance('App\Freemiom\Models\Messages\MessagesThreads', $this->messagesThreadModel);

    $messages = new Messages($this->usersModel);
    $threadList = $messages->getThreads();

Now what is the problem? Because I pass the dummy object already created, each time in the loop when the ->find method is called the same object is returned.

How should I tell mockery to return a new object with each call? Is it even possible? Or perhaps I should do some code refactoring to make it all testable?

like image 692
peterstarling Avatar asked Jan 07 '23 12:01

peterstarling


1 Answers

So to be able to return a new object with every consecutive call to the same mocked method, I had to use andReturnUsing like so:

$this->messagesThreadModel->shouldReceive('find')->times(count($threadsList))->andReturnUsing(function()
{
    $mockThreadResult = new \StdClass;
    $mockThreadResult->last = "date";

    return $mockThreadResult;
});

This will imitate the behaviour of Eloquent model that also returns new object with a find() method.

like image 169
peterstarling Avatar answered Jan 15 '23 17:01

peterstarling