尝试使用 phpunit 模拟控制器内部的 http 客户端,但它不起作用
Posted
技术标签:
【中文标题】尝试使用 phpunit 模拟控制器内部的 http 客户端,但它不起作用【英文标题】:Trying to mock an http client that is inside a controller, using phpunit, but it doesn't work 【发布时间】:2021-12-30 07:23:07 【问题描述】:我必须测试使用 Guzzle Wrapper 作为客户端从 API 获取数据的控制器的路由。该应用程序使用 Laravel 作为框架。
这是我目前正在测试的控制器功能中给我带来麻烦的代码部分:
public function addContacts(Request $request)
...
$client = new GuzzleWrapper();
$response = $client->post($uri, $data);
if($response->getStatusCode() != 200)
return response()->json("Problem getting data", 500);
...
现在,我尝试的是 getMockBuilder:
$mock = getMockBuilder(GuzzleWrapper::class)->onlyMethods(array('post'))->getMock();
$mock->expects($this->once())->method('post')->willReturn(response()->json([$responseData], 200));
http::fake:
Http::fake(
[$uri => Http::response([$responseData], 200)
);
嘲讽:
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class);
$mockGuzzleClient->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
我也尝试过这样的嘲讽:
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock)
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
);
像这样:
$this->app->instance(
GuzzleWrapper::class,
Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock)
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
)
);
在我尝试的所有操作之后,调用测试我的控制器的功能:
//Successfully add contacts to list
$this->json('POST', $addContactUri, $input_data, $token);
$this->seeStatusCode(201);
现在!无论我尝试什么,就好像 GuzzleWrapper 从未被嘲笑过,它仍然会发布并且不返回状态代码 200。无论我在谷歌上找到什么,它都不适合这种情况......有人可以帮助我吗?
【问题讨论】:
你是在使用 Laravel 还是任何其他框架,还是这个原生 php? 我正在使用 Laravel,我还使用 Mockery 在同一个测试中模拟另一个客户端,它工作得很好......虽然我不必为那个做一个 post 函数的期望 尝试只使用Http::fake
,但尝试硬编码的URL,如example.com
,并在真实代码上使用相同的URL,看看会发生什么。你得到的真正输出是什么,因此你知道它不起作用?
【参考方案1】:
Mocking 是基于容器的,为了让Laravel
获取你的模拟类,你永远不应该使用new
关键字。而是使用 resolve()
来使用容器。
$client = resolve(GuzzleWrapper::class);
这应该适用于您使用Mockery::mock()
的以下模拟方法之一。但是你模拟响应的方式,没有看到 GuzzleWrapper,我不希望它返回 Laravel 响应,否则它是自定义代码。
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock)
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
);
最正确的方法是使用Http
门面。您在控制器中的调用应如下所示。
Http::post($uri, $data);
模拟应该是这样的,一般来说,您似乎在模拟尝试中将 guzzle 和 Http
可互换的组合,并且不会飞。如果您使用 Http
模拟,请使用 Http
外观。
Http::fake([
$uri => Http::response(['your data' => 'response'], 200, []),
]);
【讨论】:
我不是编写控制器代码的人,我认为我不能更改它...有什么办法可以在不更改控制器代码的情况下做到这一点?跨度> 不,因为你不能模拟使用 new 关键字的类但是 $client = resolve(GuzzleWrapper::class);等同于 $client = new GuzzleWrapper();如果您没有更改绑定 但是模拟需要解析以上是关于尝试使用 phpunit 模拟控制器内部的 http 客户端,但它不起作用的主要内容,如果未能解决你的问题,请参考以下文章