如何在PHPUnit中Mock Guzzle请求并测试CategoryClient类
在PHPUnit中Mock CategoryClient的JSON响应并设置
$this->categories变量 我有这么一个CategoryClient类,它在构造函数里通过Guzzle的Client请求远程接口获取JSON数据,然后转成Collection赋值给私有属性$categories:
class CategoryClient { private $categories; /** * Constructor * Retrieves JSON File */ public function __construct(Client $client) { $response = $client->request('GET', config('services.url')); $this->categories = collect(json_decode($response->getBody(), true)); } }
现在想在PHPUnit测试里Mock这个JSON响应,并且能验证$this->categories被正确设置,该怎么做呢?
别担心,咱们可以通过Mock Guzzle的Client和Response对象来实现,步骤很清晰:
1. Mock Guzzle的Client实例
我们需要让Mock的Client在调用request方法时,返回我们预设的响应对象,而不是真的发HTTP请求。
2. 创建Mock的Response对象
用这个Mock响应来模拟接口返回的JSON数据,这样json_decode就能得到我们想要的数组。
3. 实例化CategoryClient并验证结果
因为$categories是私有属性,我们可以用PHPUnit的反射机制来获取它的值;如果允许修改原类,也可以加个公共getter方法,代码会更简洁。
下面是完整的测试代码示例:
use PHPUnit\Framework\TestCase; use GuzzleHttp\Client; use GuzzleHttp\Psr7\Response; use App\CategoryClient; // 替换成你的实际命名空间 class CategoryClientTest extends TestCase { public function testCategoriesAreSetFromMockedJsonResponse() { // 1. 准备我们要模拟的JSON数据 $mockJsonData = [ ['id' => 1, 'name' => 'Category 1'], ['id' => 2, 'name' => 'Category 2'], ]; $mockJsonString = json_encode($mockJsonData); // 2. 创建Mock的Response对象,返回预设的JSON字符串 $mockResponse = new Response(200, [], $mockJsonString); // 3. Mock Guzzle Client,让它的request方法返回我们的Mock响应 $mockClient = $this->createMock(Client::class); $mockClient->method('request') ->with('GET', config('services.url')) // 验证请求方法和URL是否正确 ->willReturn($mockResponse); // 4. 实例化CategoryClient $categoryClient = new CategoryClient($mockClient); // 5. 验证$categories是否被正确设置(用反射获取私有属性) $reflection = new \ReflectionClass($categoryClient); $categoriesProperty = $reflection->getProperty('categories'); $categoriesProperty->setAccessible(true); $actualCategories = $categoriesProperty->getValue($categoryClient); // 断言结果符合预期 $this->assertEquals(collect($mockJsonData), $actualCategories); } }
补充说明:
- 如果你的项目基于Laravel,还可以用
Http::fake()来更简洁地Mock请求,不过上面的方法是通用的PHPUnit方案,不依赖框架特定工具。 - 要是允许修改
CategoryClient类,建议加一个公共的getCategories()方法,这样就不用反射了:
测试里直接调用public function getCategories() { return $this->categories; }$categoryClient->getCategories()来断言即可,代码会更干净直观。
内容的提问来源于stack exchange,提问作者Jenski




