digitonic / api-test-suite
Laravel API CRUD 测试框架
Requires
- php: ^7.1|^8.0|^8.1
- illuminate/support: 7.*|8.*|9.*
- illuminate/testing: 7.*|8.*|9.*
Requires (Dev)
- orchestra/testbench: 3.8.*|^4.0|^5.0|^6.0|^7.0
- phpunit/phpunit: ^7.0|^8.0|^9.0
README
一套围绕确认您的每个API端点都设置为符合配置标准的测试工具。
安装
您可以通过composer安装此包
$ composer require digitonic/api-test-suite
安装配置和模板
$ php artisan digitonic:api-test-suite:install
使用方法
通用结构
您想要扩展以启用测试框架功能的主要类是CRUDTestCase。这是通过runBaseApiTestSuite()
方法自动断言测试套件的主要入口点。此类还提供了一个默认的setUp()
方法,用于创建用户。CRUDTestCase类扩展了您的应用中的基础Laravel TestCase,因此它还会考虑您在那里所做的所有更改,例如迁移运行、setUp或tearDown方法;
配置文件
在您运行安装命令时,此文件在config/digitonic
中生成: php artisan digitonic:api-test-suite:install
或 php artisan d:a:i
(简称)。它包含以下选项
api_user_class
这是用于您的基用户类的类。例如:App\Models\User::class
required_response_headers
一个包含头-头值对的数组,将返回所有API路由。如果任何值不存在,您可以设置头值的值为null。这可以在测试类的AssertsOuput trait的checkRequiredResponseHeaders
方法级别通过重写来覆盖。
default_headers
将传递给所有API调用的默认头。键应为大写字母,并以HTTP开头,遵循guzzle样式。例如:['HTTP_ACCEPT' => 'application/json']
entities_per_page
在此处指示索引请求中响应应具有的最大实体数。这用于分页测试。
identifier_field
一个闭包,返回一个字符串,确定ID字段的名称。您在此处可以访问与测试相同的上下文。例如:对于所有实体的uuid使用function () {return 'uuid';}
identifier_faker
一个闭包来伪造一个标识符。主要用于测试NOT_FOUND状态码。例如:用于生成payload中明天的日期的function () {return \App\Concerns\HasUuid::generateUuid();}
templates
一个数组,包含一个base_path
键,该键具有一个字符串值,指示分页和错误响应模板的路径。例如:['base_path' => base_path('tests/templates/')
creation_rules
可能是最有用的配置选项。它允许为您的有效负载创建临时的创建规则。它应该是一个数组,其中包含测试中使用的创建规则名称作为键,闭包返回适当的值作为值。例如:['tomorrow' => function () {return Carbon::parse('+1day')->format('Y-m-d H:i:s');}]
允许您在有效负载中生成明天的日期。我们将在看到creationRules()
方法时再回到它。
setUp()
方法
此方法生成
- 一个在您的测试中任何地方都可以使用的
identifierGenerator
字段,基于您的identifier_faker
配置(见上方)。 - 一个新的默认为空Laravel Collection的
entities
字段。 - 一个表示API中资源合法用户的
user
字段。这需要你在factories文件夹中为api_user_class
创建一个crud
工厂状态。这应该设置用户默认字段以及与团队/组织的关联。 - 一个使用相同工厂创建的
otherUser
字段,它代表一个不应访问所访问资源的用户。确保你的工厂crud
状态每次调用时创建不同的团队。
所有这些字段都可以在测试类的任何地方使用。
runBaseApiTestSuite()
方法
这个方法就是魔法所在。大多数经典的CRUD端点可以直接使用该方法,但一些更复杂的端点可能需要覆盖它以正确获取测试套件的上下文。如果是这种情况,所有的CRUDTestCase方法仍然可用,以帮助你构建测试。
该方法按以下顺序运行以下断言
- 未提供授权令牌时的
Unauthorized
状态码和错误格式。 - 提供不存在标识符时,资源为
Not Found
状态码和错误格式。 - 忘记必填字段时的
Unprocessable entity
状态码和错误格式。 - 尝试访问没有权限访问的资源时,返回
Forbidden
状态码和错误格式。 - 如果缺少必要头(我们需要设置系统性地设置
Accept: application/json
,这是API输出完整性的保证,否则你可能会在未经授权的请求上发生Web重定向等),则返回Bad Request
状态码和错误格式。这也可以用来强制设置其他HTTP头。如果提供了该头,它将测试该头的值。 Created
状态码,以及响应数据格式,包括提供的链接和时间戳,如果指定了,还包括id替换值(例如,使用uuid
而不是id
)。如果资源不应重复,它还会确保仅创建一次(或两次,如果可以重复)。Accepted
状态码,以及响应数据格式,包括提供的链接和时间戳(必须更新updated_at
),如果指定了,还包括id替换值(例如,使用uuid
而不是id
)。OK
状态码,以及响应数据格式,包括提供的链接和时间戳,如果指定了,还包括id替换值(例如,使用uuid
而不是id
)对于ListAll和Retrieve端点。在ListAll端点的情况下,如果测试设置为这样做,还会测试每页最大数量和格式。No Content
状态码,以及Delete端点的空响应体。
将运行哪个断言由CRUDTestCase
类中的DeterminesAssertions
Trait决定。该方法基于你通过实现CRUDTestCase抽象方法提供的关于端点的元数据。如果你发现运行了意外的断言,我鼓励你阅读该文件,并在必要时覆盖它们。
测试接口实现
为了帮助你,该包提供了一些你可以用来为创建、检索、更新、列表和删除操作设置默认值的Trait(分别是TestsCreateAction
、TestsRetrieveAction
、TestsUpdateAction
、TestsListAllAction
和TestsDeleteAction
)。这些为以下方法提供了合理的默认值:httpAction()
、statusCodes()
、shouldAssertPaginate()
、requiredHeaders()
、creationHeaders()
、requiredFields()
和cannotBeDuplicated()
。如果需要,可以在测试类中覆盖这些值。
资源元数据
resourceClass()
返回当前实体的类名。
createResource()
此方法应返回一个字符串(您创建端点路由的名称,例如 campaigns.api.store
),如果您选择使用您的API创建端点来创建测试的资源。在这种情况下,如果破坏了创建端点,则预期其他相关端点测试会失败。否则,您可以为测试设置数据库所需状态并提供一个闭包,返回目标实体,就像您的创建端点所做的那样(作为一个对象,而不是数组或JSON)。
creationRules()
这应该返回一个表单字段的关联数组。每个字段都应该使用来自API测试套件的可用规则,或者是在您的api-test-suite.php配置文件中声明的自定义规则。默认可用的规则可以在此文件中看到。
viewableByOwnerOnly()
如果您正在创建的资源仅由资源所有者可用,则应返回true;否则返回false。
cannotBeDuplicated()
如果资源可以在几个创建端点调用中用相同的有效负载重复,则应返回true;否则返回false。
##请求元数据
httpAction()
应返回以下值之一作为字符串,具体取决于端点允许的方法:get、post、put或delete。
requiredFields()
这应返回您请求的必要字段的非关联数组。对创建端点最有用。例如:['code', 'sender', 'send_at', 'is_live']
requiredHeaders()
这些是要在请求中强制执行存在的头信息。如果您使用辅助特性,则默认为配置文件中的默认头信息。例如:['HTTP_ACCEPT' => 'application/json']
creationHeaders()
如果您选择使用API创建资源(请参阅下面的createResource()
方法),则应与requiredHeaders()
返回值匹配您的创建端点。例如:['HTTP_ACCEPT' => 'application/json']
响应元数据
statusCodes()
此方法确定端点应返回哪些状态码,即测试成功和错误的不同的场景。因此,理解这一点非常重要 此方法应返回在runBaseApiTestSuite()
方法部分中的上述状态码数组。然而,辅助特性的默认值通常不是您想要的。 例如:[Response::HTTP_CREATED,Response::HTTP_UNPROCESSABLE_ENTITY,Response::HTTP_UNAUTHORIZED]
expectedResourceData(array $data)
此方法是一种声明预期值的方式。大多数时候,它将是有效负载,加上或减去一些字段。您可以从$data数组中添加这些字段,如果无法预先知道将返回什么值(例如,对于时间戳)。
expectsTimestamps()
如果API返回created_at和updated_at时间戳,则返回true;否则返回false。
expectedLinks()
返回与您的实体相关的链接数组。 例如:['self' => 'campaigns.api.show']。这尚未用于任何其他类型的链接。
fieldsReplacement()
返回一个数组,键是当前实体字段,这些字段不应公开,因此用对的值替换。 例如,['id' => 'uuid'] 以检查id不存在,而uuid在返回的API数据中存在。
shouldAssertPaginate()
这应返回true,如果端点应该分页,否则返回false。通常对ListAll端点有用。
checkRequiredResponseHeaders()
返回一个列表,其中包含服务器必须包含在端点响应中的头信息和值。如果任何值缺失,您可以设置头信息的值为null。
辅助方法和字段
您当然可以使用runBaseApiTestSuite中使用的任何子例程来编写一个更灵活、定制的端点测试套件。
CRUDTestCase类提供的一些有用方法是以下这些
-
getCurrentIdentifier()
返回根据您的配置文件设置的正在测试的实体的标识符。 -
doAuthenticatedRequest($data, array $params = [], $headers = [])
对您的端点执行配置的请求,允许您传递一个用于负载的 $data 数组,以及自定义 $params(用于构建目标 URL,例如campaignUuid
)和头部(这些将覆盖配置中设置的默认头部。如果未设置或为空,则使用默认值);它还会将测试中的 actingAs 设置为 $this->user。 -
doRequest($data, array $params = [], $headers = [])
与上述相同,但不将 actingAs 设置为 $this->user -
getResponseData(TestResponse $response)
允许您轻松提取响应中的data
属性。 -
generateEntities($numberOfEntities, $httpAction, $baseUser, $otherUser)
将为提供的 $baseUser 创建指定数量的resourceClass()
实体。此外,如果 $httpAction 是 'get' 且 $this->viewableByOwnerOnly() 返回 true,则将使用 $otherUser 创建另一个实体,该实体不属于 $baseUser,以便测试它不能被 $baseUser 看到。 -
generatePayload($user)
返回一个符合由 $this->creationRules() 返回的规则的 $user 负载。 -
generateEntityOverApi(array $payload, $user)
根据提供的负载和用户创建当前实体。这需要 createResource 方法返回一个端点名称。 -
generateUpdateData($payload, $user)
从传递给它的创建负载中生成一个更新负载。 -
identifier()
是当前上下文中的标识符键。在尝试构建自定义测试时,如果您在 api-test-suite 配置中创建identifier_field
闭包遇到问题,这会非常有用。 -
generateSingleEntity($user, $payload = null)
返回一个当前类型的实体。如果您不传递特定的负载,它将使用上述generatePayload($user)
创建一个。
分页和错误模板
使用配置文件中的 templates
字段来指示您的自动化测试套件的模板位置。默认情况下,它们将在 tests/templates
中。在运行安装程序命令时提供了默认值,以帮助您开始。
分页
分页模板当前没有默认值。如果您保持原样,则不会使用它。
错误
错误模板应该命名为您期望的状态码(您应该有以下文件:400.blade.php, 401.blade.php, 403.blade.php, 404.blade.php 和 422.blade.php
)。为了方便起见,模板允许使用正则表达式,并且 422 模板传递了两个变量:'fieldName'(在您的 requiredFields()
中正在测试的必需键)和 'formattedFieldName',这是相同字段在 snake_case 中的形式。
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志。
贡献
有关详细信息,请参阅 贡献指南。
安全
如果您发现任何与安全相关的问题,请通过电子邮件 steven@digitonic.co.uk 而不是使用问题跟踪器。
鸣谢
许可
MIT 许可证(MIT)。有关更多信息,请参阅 许可文件。