PhpUnit - 用关系模拟 laravel 模型

Posted

技术标签:

【中文标题】PhpUnit - 用关系模拟 laravel 模型【英文标题】:PhpUnit - mocking laravel model with relations 【发布时间】:2018-05-25 00:56:53 【问题描述】:

我正在尝试模拟(仅作为示例)$user->posts()->get()。

示例服务:

use App\Models\User;

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

和我的模型: 和型号:

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->user = Mockery::mock(User::class);
$this->user
    ->shouldReceive('wallets->get')
    ->andReturn('test output');

错误: TypeError:Mockery_2_App_Models_User::posts() 的返回值必须是 Illuminate\Database\Eloquent\Relations\HasMany 的实例,返回的 Mockery_4__demeter_posts 的实例

没有返回类型提示(在 post() 方法上)一切正常。我必须修改 andReturn() 吗?不知道怎么做

【问题讨论】:

该错误似乎与您的代码中的任何内容都不对应。 【参考方案1】:

可以通过使用具有有效类名的别名前缀来解决此错误。像下面这样:

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

更多信息可以查看官方文档http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#aliasing

【讨论】:

【参考方案2】:

您也可以这样使用。

use App\Models\User;

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

首先您需要模拟帖子,然后将其添加到 Collection(不要忘记在顶部 use 它)。然后,当您调用posts 属性时,它会模拟$posts。在这种情况下,它不会抛出关于返回类型的错误。

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);

【讨论】:

【参考方案3】:

我也不会在这里使用模拟。完全没有必要。所以我写的单元测试是:

创建用户。 创建一些用户创作的帖子。 对用户和帖子执行断言。

所以代码在我的测试中会是这样的:

$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);

【讨论】:

【参考方案4】:

如果你想测试这种关系,你可以:

 /** @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);


【讨论】:

这不是问的,实际上是相反的:这里你实际上是通过关系创建一个Post实例,而问题要求嘲笑关系,即:没有真正的@ 987654323@ 实例,但 $user->posts()->get() 表现得好像它是一个。

以上是关于PhpUnit - 用关系模拟 laravel 模型的主要内容,如果未能解决你的问题,请参考以下文章

Laravel PHPUnit 模拟请求

使用 Laravel 和 PHPUnit 模拟函数

在 Laravel 中使用 Mockery/phpUnit 时出错

如何用 phpunit 模拟 Laravel Eloquent 模型?

PHPUnit 问题断言模拟是使用 Laravel 的 ReflectionProperty 对象

如何在 PHPUnit / Laravel 中模拟 JSON 文件