当前位置: 首页 > news >正文

使用 Laravel 中的自定义存根简化工作

在开发与外部服务、API 或复杂功能交互的应用程序时,测试几乎总是很困难。简化测试的一种方法是使用存根类。以下是我通常使用它们的方法。

福利简介
存根是接口或类的伪实现,用于模拟真实服务的行为。它们允许您:

无需调用外部服务即可测试代码

无需 API 密钥即可在本地工作

通过避免昂贵的 API 调用来加速测试

创建可预测的测试场景

外部会计服务示例
让我们看一个外部会计服务的简单接口。实际上,你甚至不需要接口来实现这一点,但它可以更轻松地切换实现并保持同步。

interface ExternalAccountingInterface
{public function createRecord(array $data): string;
}

以下是调用外部 API 的实际实现:

class ExternalAccounting implements ExternalAccountingInterface
{public function __construct(private readonly HttpClient $client,private readonly string $apiKey,) {}public function createRecord(array $data): string{$response = $this->client->post("https://api.accounting-service.com/v1/records", ['headers' => ['Authorization' => "Bearer {$this->apiKey}",'Content-Type' => 'application/json',],'json' => $data,]);$responseData = json_decode($response->getBody(), true);return $responseData['record_id'];}
}

现在,这里有一个用于测试的虚假实现:

class FakeExternalAccounting implements ExternalAccountingInterface
{private array $createdRecords = [];private bool $hasEnoughCredits = true;public function createRecord(array $data): string{if (! $this->hasEnoughCredits) {throw new InsufficientCreditsException("Not enough credits to create a record");}$recordId = Str::uuid();$this->createdRecords[$recordId] = $data;return $recordId;}// Edge case simulationpublic function withNotEnoughCredits(): self{$this->hasEnoughCredits = false;return $this;}// Helper methods for assertionspublic function assertRecordsCreated(array $eventData): void{Assert::assertContains($eventData,$this->createdRecords,'Failed asserting that the record was created with the correct data.');}public function assertNothingCreated(): void{Assert::assertEmpty($this->createdRecords, 'Records were created unexpectedly.');}
}

之前和之后:重构以使用存根
之前:使用 Mockery

public function testCreateAccountingRecord(): void
{// Create a mock using Mockery$accountingMock = $this->mock(ExternalAccountingInterface::class);// Set expectations$accountingMock->shouldReceive('createRecord')->once()->with(Mockery::on(function ($data) {return isset($data['type']) && $data['type'] === 'invoice' &&isset($data['amount']) && $data['amount'] === 99.99;}))->andReturn('rec_123456');// Bind the mock$this->swap(ExternalAccountingInterface::class, $accountingMock);// Execute the test$response = $this->post('/api/invoices', ['product_id' => 'prod_123','amount' => 99.99,]);// Assert the response$response->assertStatus(200);$response->assertJson(['success' => true]);
}

之后:使用存根

public function testCreateAccountingRecord(): void
{// Create an instance of our custom stub$fakeAccounting = new FakeExternalAccounting;// Bind the stub$this->swap(ExternalAccountingInterface::class, $fakeAccounting);// Execute the test$response = $this->post('/api/invoices', ['product_id' => 'prod_123','amount' => 99.99,]);// Assert the response$response->assertStatus(200);$response->assertJson(['success' => true]);// Assert that records were created with the expected data$fakeAccounting->assertRecordsCreated(['type' => 'invoice','amount' => 99.99,]);
}

自定义存根可以轻松测试边缘情况和错误场景:

public function testInvoiceFailsWhenNotEnoughCredits(): void
{// Create an instance of our custom stub$fakeAccounting = new FakeExternalAccounting;// Configure the stub to simulate not enough credits$fakeAccounting->withNotEnoughCredits();// Bind the stub$this->swap(ExternalAccountingInterface::class, $fakeAccounting);// Execute the test expecting a failure$response = $this->post('/api/invoices', ['product_id' => 'prod_123','amount' => 99.99,]);// Assert the response handles the failure correctly$response->assertStatus(422);$response->assertJson(['error' => 'Insufficient credits']);// Assert that no records were created$fakeAccounting->assertNothingCreated();
}

通过此设置,您的本地开发环境将使用虚假实现,让您无需 API 密钥即可工作,也不用担心速率限制。当部署到暂存区或生产环境时,应用程序将使用真实的实现

查看

相关文章:

  • 计算机组成原理-存储器的概述
  • 快速上手 Metabase:从安装到高级功能实战
  • 解决国产GD32下载的工程无法编译
  • 基于 GWAS 的群体遗传分析将 bZIP29 确定为玉米中的异种基因
  • SpringBoot 配置加载顺序?
  • Cursor学习-Java环境配置
  • 不等式是否满足约束并输出最大差 - 华为OD机试真题(JavaScript 题解)
  • 运维_集运维核心学习
  • MCP详解及协议的使用(python版本和Node版本)
  • AGV|无人叉车工业语音播报器|预警提示器LBE-LEX系列性能与接线说明
  • 《光子技术成像技术》第二章 预习2025.6.7
  • 低代码平台前端页面表格字段绑定与后端数据传输交互主要有哪些方式?华为云Astro在这方面有哪些方式?
  • 坚持每日Codeforces三题挑战:Day 4 - 题目详解(2025-06-07,难度:1000, 1100, 1400)
  • [AI绘画]sd学习记录(二)文生图参数进阶
  • 分享一道力扣
  • 实习学习项目
  • 6.7 打卡
  • OCR MLLM Evaluation
  • pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
  • Ubuntu2404 下搭建 Zephyr 开发环境
  • 使用云主机做网站教程/搜索引擎优化的基础是什么
  • 网站建设基础及流程/深圳纯手工seo
  • 网站建设阝金手指信誉/google chrome浏览器
  • 网站建设公司税率/打造龙头建设示范
  • 珠宝怎么做网站/营销型网站的类型有哪些
  • wordpress怎么设置语言设置/济南seo网站关键词排名