Phpunit 用find()模仿雄辩的模型

Phpunit 用find()模仿雄辩的模型,phpunit,laravel,laravel-4,Phpunit,Laravel,Laravel 4,我试图用嘲弄来嘲弄雄辩的模型。模型通过以下方式注入控制器: __construct(Post $model){$this->model=$model} 现在我在控制器中调用find()函数 $post = $this->model->find($id); 这是对PostsController的测试 class PostsTest extends TestCase{ protected $mock; public function setUp()

我试图用嘲弄来嘲弄雄辩的模型。模型通过以下方式注入控制器:

__construct(Post $model){$this->model=$model}
现在我在控制器中调用
find()
函数

$post = $this->model->find($id);
这是对PostsController的测试

class PostsTest extends TestCase{

      protected $mock;

      public function setUp() {
        parent::setUp();
        $this->mock = Mockery::mock('Eloquent', 'Posts'); /*According to Jeffrey Way you have to add Eloquent in this function call to be able to use certain methods from model*/
        $this->app->instance('Posts', $this->mock);
      }

      public function tearDown() {

        Mockery::close();
      }

      public function testGetEdit()
      {
        $this->mock->shouldReceive('find')->with(1)->once()->andReturn(array('id'=>1));

        $this->call('GET', 'admin/posts/edit/1');

        $this->assertViewHas('post', array('id'=>1));
      }
    }
运行PhpUnit时出现错误:

Fatal error: Using $this when not in object context in ...\www\l4\vendor\mockery\mockery\library\Mockery\Generator.php(130) : eval()'d code on line 73
这显然是因为
find()
被声明为静态函数。现在,代码可以正常工作,所以我怎样才能成功地模拟雄辩的模型而不失败呢。因为我们依赖依赖依赖注入,所以我必须非静态地调用
find()
,否则我只能执行
Post::find()

我提出的一个解决方案是在
BaseModel

public function nsFind($id, $columns = array('*'))
{
  return self::find($id, $columns);
}
但是这是一个很大的痛苦,因为函数必须有不同的名称


是我做错了什么,还是你有更好的想法?

我认为这就是原因,杰弗里在他的书《拉威尔测试解码》(第10章)中介绍了存储库


Mockry在其自述文件中也有关于静态方法的部分,请参见

模仿雄辩的模型是一件非常棘手的事情。这本书中有介绍,但我特别指出这是权宜之计。最好使用存储库


但是,要回答您的问题,问题是您没有在构造函数中执行模拟。这是让它发挥作用的唯一方法。这并不理想,我也不推荐。

有一种方法可以做到这一点。(Laravel版本为5.8)

假设您有一个基类:

<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model as EloquentModel;
use Mockery\Mock;

/**
 * Base class of all model classes, to implement Observers or whatever filters you will need
 */
class Model extends EloquentModel
{

    protected static $mocks = [];

    /**
     * @return Mock
     */
    public static function getMock()
    {
        if (isset(self::$mocks[static::class])) {
            return self::$mocks[static::class];
        }
        self::$mocks[static::class] = \Mockery::mock(static::class)->makePartial()->shouldAllowMockingProtectedMethods();
        return self::$mocks[static::class];
    }

    public static function removeMock(): void
    {
        if (isset(self::$mocks[static::class])) {
            unset(self::$mocks[static::class]);
        }
    }

    public static function deleteMocks() : void
    {
        self::$mocks = [];
    }

    public static function __callStatic($method, $parameters)
    {
        /**
         * call the mock's function
         */
        if (isset(self::$mocks[static::class])) {
            return self::$mocks[static::class]->$method(...$parameters);
        }
        return parent::__callStatic($method, $parameters);
    }


}

我在读这本书(好书)。然而,Jeffrey没有提到这个问题,在Jeffrey的Generator Helper包中,他引入了新的generate:scaffold artisan命令,该命令生成PHPUnit测试,他使用这种样式(没有存储库)并调用find()函数。现在,这些测试也失败了!我见过其他使用find方法的资源,但是没有人提到任何东西由于answer实现了Jeferry Way下面的答案,所以书中的代码是否有效?因为我已经按照书中所述进行了布局,并且我从书中概述的BaseModel中得到了一个类似的错误,我正在尝试使用BaseModelTestI进行测试。我想听听你对Jeffrey的看法。使用存储库模式不是又一次创建了轮子(雄辩的)吗?关系如何,使用用户模型的包如何?我只是不理解存储库模式的这一部分。
<?php
namespace App\Model;

class Accounts extends Model
{
    /**
     * @var  string
     */
    protected $table = 'accounts';
    /**
     * @var  string
     */
    protected $primaryKey = 'account_id';

    /**
     * attributes not writable from outside
     * @var  mixed
     */
    protected $guarded = ['account_id'];
}
<?php


namespace App\Service;

use App\Model\Accounts;

class AccountService
{
    public function getAccountById($id): ?Accounts
    {       
       return Accounts::find($id);       
    }

}
<?php
namespace Tests\Unit\Services;

use App\Model\Accounts;
use App\Service\AccountService;
use Tests\TestCase;

class AccountServiceTest extends TestCase
{
    public function testGetAccountById()
    {
        $staticGlobalAccountMock = Accounts::getMock();
        $staticGlobalAccountMock->shouldReceive('find')
                ->andReturn(new Accounts(
                        ['account_id' => 123, 
                         'account_fax' => '056772']));
        $service = new AccountService();
        $ret = $service->getAccountById(123);
        $this->assertEquals('056772',$ret->account_fax);
        //clean up all mocks or just this mock
        Accounts::deleteMocks();
    }

}