嘲弄模拟不返回指定值

Posted

技术标签:

【中文标题】嘲弄模拟不返回指定值【英文标题】:Mockery mock not returning specified value 【发布时间】:2015-06-21 07:49:32 【问题描述】:

我在我的 Laravel 项目中使用 Mockery 来模拟 User Eloquent 模型并测试路由。

这就是我测试/api/user/activate 路由的方式:

<?php

use Illuminate\Support\Facades\Session;

class ActivateTest extends TestCase

    private $userMock;

    public function setUp()
    
        parent::setUp();

        $this->userMock = Mockery::mock('App\User');

        Session::start();
    

    public function tearDown()
    
        Mockery::close();
    

    public function testActivate()
    
        $this->userMock->shouldReceive('where->first')->once()->andReturn('test');
        $this->userMock->shouldReceive('activate')->once();

        $response = $this->call('POST', '/api/user/activate', [
            'activationToken' => '838jfjnvu83u3',
            '_token' => csrf_token()
        ]);

        // This will be displayed in the PHPunit output
        print_r($response->getContent());

        $this->assertResponseStatus(200);
    

我遇到的问题是andReturn('test') 似乎不起作用。 PHPunit 结果是:

F"error":"message":null,"statusCode":404

Time: 276 ms, Memory: 15.50Mb

There was 1 failure:

1) ActivateTest::testActivate
Failed asserting that 404 matches expected 200.

这是UserControlleractivate()的内容:

public function activate(Request $request)

    $activation = $request->input();

    $user = $this->user->where('activationToken', $activation['activationToken'])->first();

    if(!$user) return $this->respondNotFound($user);

    try
    
        $user->activate($activation['password']);
    
    catch(ModelException $e)
    
        return $this->respondInternalError($e->errorMessages());
    ;

    return $this->respondCreated('Account activated.');

问题是控制器中的$usernull,因为模拟没有返回test(在这种情况下,条件将评估为真,我不会得到404 响应)。

编辑:

我也尝试过使用 PHPunit 模拟,但没有成功:

$this->userMock = $this->getMockBuilder('App\User')->setMethods(['where', 'first', 'activate'])->getMock();

$this->userMock->expects($this->once())->method('where')->willReturn($this->userMock);
$this->userMock->expects($this->once())->method('first')->willReturn('test');
$this->userMock->expects($this->once())->method('activate');

【问题讨论】:

我对 Mockery 还不是很了解,但我认为 where-&gt;first 是不可能的,因为它不是一个有效的函数。在user 对象上调用的函数/属性是where,它会返回一个你在上面调用first() 的对象。 看看this question,看来我可以做到。 我在提供的问题中没有看到任何..-&gt;shouldReceive('where-&gt;first')。只有存根答案有一些关于它的东西,但那是一个扩展或其他东西。 如果你不关心参数,你可以做 shouldReceive('select->where->runQuery->fetch')。 无论如何,分析代码我注意到了什么可能导致问题。可能是函数first() 实际上不是一个雄辩的模型函数这一事实导致了这个问题。 啊,谢谢,不知道链接是可能的。会试试看! 【参考方案1】:

仅仅模拟一个对象是不够的。您需要将该模拟对象注入到包含 activate() 函数的类中。

您也可以在 setUp() 函数中执行此操作。试试添加这个...

$this->app->instance('App/User', $this->userMock);

这将告诉 Laravel 当你想要注入 App/User 的实例时,改为注入你刚刚创建的模拟对象。

【讨论】:

【参考方案2】:

问题是由-&gt;first() 引起的,因为它不是EloquentUser 类中不存在的方法。

为了解决这个问题,我创建了一个新的UserRepository 并将其作为依赖项注入到控制器构造函数中。

class UserRepository implements UserRepositoryInterface

    /**
     * @var User
     */
    protected $user;

    /**
     * @param User $user
     */
    public function __construct(User $user)
    
        $this->user = $user;
    

    /**
     * @param $activationToken
     * @return mixed
     */
    public function whereActivationToken($activationToken)
    
        return $this->user->where('activationToken', $activationToken)->first();
    

UserController中注入:

public function __construct(UserRepository $userRepository)

    $this->userRepository = $userRepository;

这就是测试PostActivateTest 类现在的样子:

use Illuminate\Support\Facades\Session;

class PostActivateTest extends TestCase

    private $user;
    private $userRepositoryMock;

    public function setUp()
    
        parent::setUp();

        $this->user               = Mockery::mock('App\User');
        $this->userRepositoryMock = Mockery::mock('Repository\Database\UserRepository');
        $this->app->instance('App\User', $this->user);
        $this->app->instance('Bloom\BloomCRM\Repository\Database\UserRepository', $this->userRepositoryMock);

        Session::start();
    

    public function tearDown()
    
        Mockery::close();
    

    public function testActivate()
    
        $this->userRepositoryMock->shouldReceive('whereActivationToken')->once()->andReturn($this->user);
        $this->user->shouldReceive('activate')->once();

        $this->call('POST', '/api/user/activate', [
            'activationToken' => '838jfjnvu83u3',
            'password' => 'test',
            '_token' => csrf_token()
        ]);

        $this->assertResponseStatus(201);
    

【讨论】:

以上是关于嘲弄模拟不返回指定值的主要内容,如果未能解决你的问题,请参考以下文章

嘲弄如何断言类实例

嘲弄用 andReturn 返回新对象

嘲弄 os.type() 不起作用

嘲弄:模拟对象上不存在方法

模拟数据库查询 laravel 嘲弄

如何模拟另一个文件中的返回值?