用于 PHPunit 的 Laravel 5.4 模型模拟

Posted

技术标签:

【中文标题】用于 PHPunit 的 Laravel 5.4 模型模拟【英文标题】:Laravel 5.4 Model mock for PHPunit 【发布时间】:2017-09-04 21:20:19 【问题描述】:

这是我第一次使用 Mockery for phpUnit。我按照这个论坛的例子,仍然得到这个错误:

Mockery\Exception\InvalidCountException:方法 all() 来自 Mockery_0_App_Card 应该被调用 1 次,但调用 0 次。

基本上,我将我的模型注入到我的控制器中。像这样:

class CardController extends Controller
   
    protected $repository;

    function __construct(Model $repository)
    
        $this->repository = $repository;
    

    public function index()
    

       $data = $this->repository->all();
       return $data;
    

并尝试像这样进行测试:

class CardTest extends TestCase

    protected $mock;

    protected function setUp(): void
    
        parent::setUp();

        $this->mock = Mockery::mock('Model', '\App\Card');
    

    public function testCardsList()
    

        $this->mock->shouldReceive('all')
                    ->once()
                    ->andReturn(json_encode([[
                        "id"=> 1,
                        "name"=> "Aut modi quasi corrupti.",
                        "content"=> "..."
                        ],
                        [
                        "id"=> 2,
                        "name"=> "Voluptas quia distinctio.",
                        "content"=> "..."
                    ]]));            

        $this->app->instance('\App\Card', $this->mock);

        $response = $this->json('GET', $this->api.'/cards');
        $this->assertEquals(200, $response->status(), 'Response code must be 200');    
    

我尝试了几种变体,但总是一样。例如,在控制器中设置 Mockery 或使用 Card::class 表示法。有什么线索吗?

另外,我确定响应是从数据库中提取数据,而不是使用我提供的数组。所以,嘲弄对我的模型没有影响。

【问题讨论】:

您正在调用“索引”函数而不是“全部”。 “all”功能属于模型存储库而不是 App\Card 试试这个。 $this->mock->shouldReceive('index') 同理:“来自 Mockery_0_App_Card 的方法 index() 应该被准确地调用 1 次,但被调用 0 次。”。无论如何,据我了解,我应该使用“全部”,因为涉及模型/模拟方法。另一方面,“index”只是控制器方法。 【参考方案1】:

在阅读了一些资料后,我确信使用 SQLite 数据库进行测试比为模型创建模型要好得多。您不必花费太多时间来创建模型。我链接到一些关于如何实现测试环境的讨论线程,但我也粘贴了我最终编写的代码。

基本上,您必须将数据库配置为 SQLite。您将声明它将在内存中运行。这比使用文件要快得多。

然后,您想要运行您的迁移。在我的情况下,还 seed DB。

<?php

namespace Tests;

use DirectoryIterator;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Config;

abstract class TestCase extends BaseTestCase

    use CreatesApplication;

    protected function setUp()
    
        parent::setUp();

        Config::set('database.connections.sqlite.database', ':memory:');
        Config::set('database.default', 'sqlite');

        Artisan::call('migrate');
        Artisan::call('db:seed');

    protected function tearDown()
    
        Artisan::call('migrate:reset');
        parent::tearDown();
    

有一个警告:每次测试都会调用一次setUp()。而且我发现这种表示法不起作用,因为每次都会重新生成数据库: @depends testCreateCard

我使用该表示法将 ID 从 testCreate() 传递给其他几种方法。但最终信任我的种子并使用硬编码值。

参考:

    Laravel 5 - Using Mockery to mock Eloquent model https://www.patrickstephan.me/post/setting-up-a-laravel-5-test-database.html https://laracasts.com/discuss/channels/testing/how-to-specify-a-testing-database-in-laravel-5 https://laracasts.com/discuss/channels/general-discussion/how-to-migrate-a-testing-database-in-laravel-5

【讨论】:

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

升级到 5.4 后在 Laravel BrowserKit 包中找不到 PHPUnit\Framework\Constraint\Constraint

Laravel 5.4 phpunit 与黄昏测试 Env App_Url

反射异常:Laravel 5.4

使用 phpunit.xml、.env.dusk.local 和 sqlite 内存数据库设置 Laravel 5.4 和 Dusk

phpunit 命令不适用于 Windows 7 上的 laravel 4

如何创建仅用于测试的 Laravel 路由(phpunit)?