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
相关产品推荐
相关产品推荐

