You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel框架下PHPUnit模拟控制器内GuzzleWrapper客户端失败的问题求助

问题根源与解决方案

我一眼就看出问题所在了——你在控制器里直接new GuzzleWrapper(),这完全绕过了Laravel的服务容器,导致你所有的Mock操作都白做了!容器里的Mock实例和控制器自己new出来的对象根本不是同一个,所以实际的POST请求还是会被执行。

第一步:重构控制器,使用依赖注入

要让Mock生效,必须让Laravel容器来管理GuzzleWrapper的实例,最简单的方式是通过构造函数注入:

use App\Services\GuzzleWrapper; // 替换成你的GuzzleWrapper实际命名空间
use Illuminate\Http\Request;

class YourController extends Controller
{
    protected $guzzleWrapper;

    // 构造函数注入依赖
    public function __construct(GuzzleWrapper $guzzleWrapper)
    {
        $this->guzzleWrapper = $guzzleWrapper;
    }

    public function addContacts(Request $request) { 
        // ... 
        // 不再手动new,改用注入的实例
        $response = $this->guzzleWrapper->post($uri, $data); 
        if($response->getStatusCode() != 200) { 
            return response()->json("Problem getting data", 500); 
        } 
        // ... 
    }
}

第二步:正确编写测试用例(Mockery版本)

现在容器可以接管GuzzleWrapper的实例了,我们可以正确Mock它的post方法:

use Mockery;
use App\Services\GuzzleWrapper;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;

class YourControllerTest extends TestCase
{
    use RefreshDatabase, WithFaker;

    protected function tearDown(): void
    {
        parent::tearDown();
        // 清理Mockery的实例,避免测试污染
        Mockery::close();
    }

    public function test_add_contacts_returns_201_on_success()
    {
        $responseData = ['id' => 123, 'status' => 'success'];
        $addContactUri = '/api/contacts/add'; // 替换成你的实际路由
        $inputData = ['email' => 'test@example.com', 'name' => 'Test User'];
        $authToken = ['Authorization' => 'Bearer ' . $this->createUser()->createToken('test')->plainTextToken];

        // 1. 创建GuzzleWrapper的Mock
        $mockGuzzle = Mockery::mock(GuzzleWrapper::class);
        $mockGuzzle->shouldReceive('post')
            ->once() // 验证post方法被调用一次
            ->with($this->anything(), $this->equalTo($inputData)) // 可选:验证传入的参数是否正确
            ->andReturn(response()->json($responseData, 200));

        // 2. 将Mock绑定到Laravel容器中
        $this->app->instance(GuzzleWrapper::class, $mockGuzzle);

        // 3. 发送请求并断言结果
        $this->json('POST', $addContactUri, $inputData, $authToken)
             ->assertStatus(201);
    }
}

为什么之前的方法无效?

你之前的所有Mock操作都是针对Laravel容器内的GuzzleWrapper实例,但控制器里的new GuzzleWrapper()会创建一个完全独立的新对象,和容器里的Mock没有任何关联,所以实际的API请求还是会被触发。只有通过依赖注入让容器来解析实例,Mock才能生效。

应急方案(不推荐重构时使用)

如果暂时不想重构控制器,可以用Mockery的静态Mock来覆盖类的构造函数,但这种方式会带来测试污染,不推荐长期使用:

public function test_add_contacts_应急方案()
{
    $responseData = ['id' => 123];
    // 用overload前缀模拟类的构造函数
    Mockery::mock('overload:' . GuzzleWrapper::class)
        ->shouldReceive('post')
        ->once()
        ->andReturn(response()->json($responseData, 200));

    // 后续测试代码不变
    $this->json('POST', $addContactUri, $input_data, $token)
         ->assertStatus(201);
}

内容的提问来源于stack exchange,提问作者DreamyT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 02:37:45