readdle / stripe-httpclient-mock
这是 Stripe 的 HttpClient 的模拟,可用于测试目的,以便测试您的代码,而不执行实际的 HTTP 请求
Requires
- ext-json: *
- stripe/stripe-php: >=7.56
Requires (Dev)
- phpunit/phpunit: ^9
This package is auto-updated.
Last update: 2024-09-20 12:03:51 UTC
README
这是一个相当原始的库,旨在为 stripe-php 库提供完整的 HTTP 客户端模拟,以便在没有向 Stripe 发送实际 HTTP 请求的情况下执行测试。
目前,此库仅覆盖了 stripe-php 功能的一部分,但希望有一天它将得到充分的发展,以覆盖 stripe-php 所提供的一切。
此库背后的主要思想是提供一个具有状态的 "服务器",它能够记住之前被询问的内容。状态不会在生命周期之间保存,但在单个生命周期内状态将被保留。这是与官方 Stripe 服务器模拟的主要区别。此库存在的另一个原因是避免在您用不同编程语言编写的应用程序中有一个单独的组件,因此需要另一个容器(或在无容器情况下的环境配置)。
参与此项目
"覆盖 stripe-php 库的所有功能" 这一任务真的是非常巨大的。这是可能的,但需要巨大的努力(任务有多大,努力就有多大),而且在您忙于主要项目时,这并不总是可能的。
因此,强烈欢迎对以下任何方式感兴趣的人参与此项目
- 您有关于如何改进此项目的想法,或者您看到有什么错误?请不要犹豫,提交一个 issue。
- 如果您有时间并有意愿添加/改进功能或修复一个错误,您的 pull request 将非常有价值
如果您只覆盖了您使用的 Stripe API 的一部分,那么将来可能会覆盖整个 API。
安装
这里没有特别之处,只需使用 composer 安装软件包即可
composer install readdle/stripe-httpclient-mock
使用方法
使用方法非常简单,只需在 PHPUnit 配置的 bootstrap 脚本中两行代码
Readdle\StripeHttpClientMock\HttpClient::$apiKey = "your_api_key_goes_here";
Stripe\ApiRequestor\ApiRequestor::setHttpClient(new Readdle\StripeHttpClientMock\HttpClient());
这就完成了,现在您已经有了 stripe-php 的 HTTP 客户端实例模拟,它将“与”代码片段“通信”,而不是执行真正的 HTTP 请求。
总体结构
文件
src/Collection.php - 实体的集合表示
src/EntityManager.php - 负责创建/更新/删除/列出实体、执行搜索和分页功能的管理器
src/HttpClient.php - HTTP 客户端,用于替代 stripe-php 基于 curl 的客户端
目录
src/Entity - 实现的实体,每个实体都必须扩展 AbstractEntity 类
src/Error - 错误响应,每个错误都必须扩展 AbstractError 类
src/Success - 成功响应,不包含实体,但包含有关操作及其结果的信息,必须实现 ResponseInterface
实体类结构
属性
每个实体必须至少有一个 $props 属性被填充,其中包含该实体拥有的所有字段(每个实体的字段列表可以在 Stripe 文档 中找到)。
前缀
在大多数情况下,实体将重写方法 prefix() 以确保新生成的ID具有正确的前缀。
以下内容是可选的,应在需要时应用。然而,为了覆盖所有功能,最好在适用的情况下遵循这些说明。
实体的创建
如果实体的创建需要任何额外操作,您可以重写 create() 方法,在创建发生之前或之后执行某些操作。通常,这用于创建相关实体。
可扩展属性
实体可能具有属性 $expandableProps,其中包含可以扩展的字段(在文档中标记为“可扩展”)。默认情况下,如果字段列在此数组中,并且请求包含需要扩展此字段的 expand 参数,实体将尝试自动扩展此字段。字段名称将用作子实体名称(类),其值将用作ID。这是一个递归操作,因此当 expand 参数需要扩展已扩展字段中的字段时,它将传递给后续实体并以相同方式扩展。
当字段扩展的逻辑更复杂时,您可以重写 howToExpand() 方法,并在其中实现针对实体的特定逻辑。请记住,在您的实现未涵盖的情况下,请调用父方法。目前,此方法应返回两种可能的数组类型
['target' => 'expandableProp', 'object' => 'entity_name', 'params' => []]
这告诉库使用主对象的字段值作为其ID来查找具有 entity_name 的实体。Params将传递给 retreiveEntity(),因此可以进行额外的过滤。
['target' => 'objectSearcher', 'object' => 'entity_name', 'searcher' => function ($mainEntity, $entities) {} ]
这告诉库将主实体(正在扩展的字段所在的实体)和整个 entity_name 类的实体集合传递给搜索函数,以便搜索函数可以自己找到合适的实体。
子操作
实体可能具有属性 $subActions,其中填充了所有可用的子操作。子操作是对实体执行的操作,该操作不遵循(由于不可能)REST概念。例如
POST /v1/payment_methods/:id/attach
POST /v1/payment_methods/:id/detach
这些不是创建/更新/删除操作,而是在特定实体上执行额外操作。
$subActions 是一个关联数组,其中键是操作(在请求中所述),值是负责执行此操作的方法的名称。该方法应返回 ResponseInterface 的实现,并将其作为“API响应”返回给 stripe-php 客户端。
如果此功能不能满足实体的行为,您可以重写 subAction() 方法并为此实体实现自定义逻辑。不要忘记在父方法中调用,以覆盖常规情况。
子实体
实体可能具有属性 $subEntities,其中填充了其子实体的实体名称。子实体是通过主实体访问的实体,请参阅以下示例
POST /v1/customers/:id/tax_ids
GET /v1/customers/:id/tax_ids/:id
DELETE /v1/customers/:id/tax_ids/:id
GET /v1/customers/:id/tax_ids
尽管这些请求是在遵循REST概念的情况下在customer实体上执行的,但实际上它们都是在具有指定ID的客户的tax_id实体上执行的。因此,库将这些操作转换为(在这个特定示例中)它们是在tax_id实体上执行的方式,但使用客户的customer ID作为过滤器(或是在创建tax_id实体时用于适当字段的值)。
未覆盖的请求
可能(甚至很可能)存在一些特定请求未被此库覆盖。如果请求的URL与常规URL不同(在实体上执行操作,在实体上执行子操作或在子实体上执行操作),您可以覆盖相应实体的parseUrlTail()方法并添加解析此特定请求的逻辑。但最有可能的是,库无法处理此结果,因此有两种首选的解决方法
- 打开一个问题并请求库缺少的功能,并详细描述如何实现
- 改进库并创建一个包含覆盖此特定情况的代码的pull request
这两种方法都将会得到高度赞赏,并且一旦可能就会得到回应。