在 PHPUnit 中调用路由时如何在 Laravel 8 中模拟 Eloquent 模型

How to mock Eloquent Model in Laravel 8 when calling a route in PHPUnit

我正在尝试模拟模型中的方法,以便我能够使用不同的场景测试控制器 api 端点。

我目前正在使用 Laravel 8 PhpUnit 和 Mockery 包。 在路由中我使用的是路由模型绑定。

api.php

Route::get('/api/{project}', [ProjectController::class, 'show']);

ProjectController.php

class ProjectController extends Controller 
{
    public function show(Project $project)
    {
        $state = $project->getState();
        
        return response()->json($state);
    }
}

ProjectControllerTest.php

class ProjectControllerTest extends TestCase
{
    /**
     * @test
     * @group ProjectController
     */
    public function shouldReturn200ForValidProjectGuid()
    {
        $project = Project::factory()->create();

        $mock = Mockery::mock(Project::class);

        // I have also tried this, see error below
        // $mock = Mockery::mock(Project::class)->makePartial();

        $this->app->instance(Project::class, $mock);

        $mock->shouldReceive('getState')->andReturn('New South Wales');

        $response = $this->getJson("/api/{$project->guid}");
        
        $response->assertStatus(200)
            ->assertJson([
                'data' => 'New South Wales'
            ]);
     }
}

我目前遇到这个错误

Received Mockery_0_App_Models_Project::resolveRouteBinding(), but no expectations were specified Exception caught at [/usr/src/app/vendor/mockery/mockery/library/Mockery/Loader/EvalLoader.php(34) : eval()'d code@924

我尝试了其他选项,例如 makePartial(),但它也会导致以下错误

Received Mockery_0_App_Models_Project::__construct(), but no expectations were specified Exception caught at [/usr/src/app/vendor/mockery/mockery/library/Mockery/Loader/EvalLoader.php(34) : eval()'d code@924

非常感谢任何帮助,谢谢

您没有模拟模型,您已经在使用 Factory 所以您仍然使用它在您的模型上创建“模拟”数据。

$project = Project::factory()->create(['state' => 'New South Wales']);

阅读 Laravel 中有关工厂和单元测试的更多信息。看官方文档。

您可以尝试以下方法。我稍微更改了控制器响应以使其与 assertJson().

一起使用

web.php

Route::get('/projects/{project}', [ProjectController::class, 'show']);

项目控制器

<?php

namespace App\Http\Controllers;

use App\Models\Project;

class ProjectController extends Controller
{
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show(Project $project)
    {
        return response()->json([
            'state' => $project->getState()
        ]);
    }
}

ProjectControllerTest

<?php

namespace Tests\Feature;

use App\Models\Project;
use Mockery;
use Tests\TestCase;

class ProjectControllerTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function test_example()
    {   
        $mock = Mockery::mock(Project::class)->makePartial();

        $mock->shouldReceive('resolveRouteBinding')->andReturnSelf();
        $mock->shouldReceive('getState')->andReturn('NSW');

        $this->app->instance(Project::class, $mock);

        $response = $this->get('/projects/1');

        $response->assertStatus(200)
            ->assertJson([
                'state' => 'NSW'
            ]);
    }
}