【问题标题】:How to mock Eloquent Model in Laravel 8 when calling a route in PHPUnit在 PHPUnit 中调用路由时如何在 Laravel 8 中模拟 Eloquent 模型
【发布时间】:2021-09-14 04:40:25
【问题描述】:

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

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

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

任何帮助将不胜感激,谢谢

【问题讨论】:

  • 为什么不使用标准模拟 phpunit $stub = $this->createMock(SomeClass::class) ?
  • @АлександрЧерножуков 我试过用它,但还是不行,你能提供一个例子吗
  • 我读到了在mock上调用resolveRouteBinding方法的错误信息,但是mock不知道如何处理这个调用,因此抛出了异常。在让系统使用模拟之前,实际指定这个缺失的期望会有所帮助。
  • @hakre 你能提供一个例子吗

标签: php laravel eloquent phpunit mockery


【解决方案1】:

您可以尝试以下方法。我稍微更改了控制器响应以使其与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'
            ]);
    }
}

【讨论】:

  • 这不是一个有效的答案,因为您没有模拟模型......但无论如何......
【解决方案2】:

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

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

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

【讨论】:

  • 状态不是项目模型的一部分,这是模型中调用的外部数据库,我只想模拟该方法以返回不同的东西。否则我会改用工厂
  • 好吧,所以你还是不模拟一个模型,你模拟模型里面的东西,你能显示模型代码吗?
猜你喜欢
  • 2021-12-23
  • 2015-03-12
  • 2018-05-25
  • 2014-11-05
  • 2018-10-10
  • 2017-01-13
  • 2017-09-04
  • 2020-10-17
  • 2016-08-17
相关资源
最近更新 更多