1
votes

I'm trying to mock (it's example only) $user->posts()->get().

example service:

use App\Models\User;

class SomeClass{
    public function getActivePost(User $user): Collection
    {
        return $user->posts()->get();
    }
}

and my Model: and Model:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use \App\Models\Post;

class User extends Model
{
    public function posts() : HasMany
    {
        return $this->hasMany(Post::class);
    }
}

this doesn't work:

$this->user = Mockery::mock(User::class);
$this->user
    ->shouldReceive('wallets->get')
    ->andReturn('test output');

error: TypeError: Return value of Mockery_2_App_Models_User::posts() must be an instance of Illuminate\Database\Eloquent\Relations\HasMany, instance of Mockery_4__demeter_posts returned

without return type hint (on post() method) everything is ok. Must I modify andReturn()? idk how

4
That error doesn't seem to correspond with anything in your code. - Devon

4 Answers

1
votes

This error can be solved by using the alias prefix with a valid class name. Like the following:

$m = m::mock('alias:App\Models\User');

More information can be found at the official documentation http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#aliasing

1
votes

Alternatively you can use like this.

use App\Models\User;

class SomeClass{
    public function getActivePost(User $user): Collection
    {
        $user->load('posts');
        return $user->posts;
    }
}

First you need to mock post, then add it to Collection (don't forget to use it in the top). Then when you call posts attribute its takes mocked $posts. In this case it will not throw error about return type.

use Illuminate\Database\Eloquent\Collection;


$post = $this->mock(Post::class)->makePartial();
$posts = new Collection([$post]);

$this->user = Mockery::mock(User::class);
$this->user
    ->shouldReceive('getAttribute')
    ->with('posts');
    ->andReturn($posts);
-1
votes

Also i wouldn't use mocks here. There is absolutely no need for it. So the unit test i write would be:

  • Create a user.
  • Create some posts authored by the user.
  • Perform assertions on user & posts.

So the code will then be something like this in my test:

$user = factory(User::class)->create();
$posts = factory(Post::class, 5)->create(['user_id' => $user->id]);

$this->assertNotEmpty($user->id);
$this->assertNotEmpty($posts);

$this->assertEquals(5, $posts->fresh()->count());
$this->assertEquals($user->id, $post->fresh()->first()->user_id);
-1
votes

if you want to test the relationship you can:

 /** @test */
function user_has_many_posts()
{
    $user = factory(User::class)->create();
    $post= factory(Post::class)->create(['user_id' => $user->id]);

    //Check if database has the post..
    $this->assertDatabaseHas('posts', [
        'id' => $post->id,
        'user_id' => $user->id,
    ]);
    //Check if relationship returns collection..
    $this->assertInstanceOf('\Illuminate\Database\Eloquent\Collection', $user->posts);

}