问题简介
想象一下这样的情况:你(一个 Python 开发人员)开始了一份新工作或加入一个新项目,你被告知文档不是最新的,甚至不存在,而那些编写代码的人很久以前就辞职了。此外,代码是用您不熟悉的语言(或“您不知道的”)编写的。你打开代码,开始检查它,发现也没有测试。此外,该服务已经在 Prod 上工作了很长时间,以至于您害怕更改某些内容。
我不是在谈论任何特定的项目或公司。我至少经历过三次。
黑盒子
好吧,你有一个黑匣子,它有 API 方法(从代码判断),你知道它会拉取一些东西并写入数据库。还有接收请求的服务的文档。
优点包括它从那里开始,它拉取的 API 上有文档,并且服务代码非常可读。至于缺点,它想通过API获得一些东西。有些东西可以在容器中运行,有些东西可以在开发人员环境中使用,但不是所有的东西。
另一个问题是,对黑匣子的请求以及从黑匣子到其他一些服务的请求都是加密和签名的。
同时,您需要更改此服务中的某些内容,而不是破坏正在工作的内容。
在这种情况下,Postman 或 cURL 使用起来很不方便。您需要在每种特定情况下准备每个请求,因为存在动态输入数据和签名,具体取决于请求的时间。
几乎没有现成的测试,如果你不太了解这门语言,就很难写出来。
市场提供的解决方案允许您在此类服务中运行测试。但是,我从未使用过它们,因此尝试理解它们会比创建自己的解决方案更加困难,并且花费更多的时间。
创建的解决方案
我想出了一个简单方便的选择。我用 Python 编写了一个简单的脚本来拉动这个应用程序。
我使用了请求和一个简单的签名,这是我为预先准备的请求快速创建的。
接下来,我需要模拟后端。
第一选择
为此,我刚刚在 Python 中运行了一个模拟服务。就我而言,Django 被证明是最快、最简单的工具。
我决定尽可能简单快速地实现所有内容,并使用最新版本的 Django。结果相当不错,但这只是一种方法,尽管我想节省时间,但我还是花了几个小时才使用。有几十种这样的方法。
配置文件示例
最后,我摆脱了所有不需要的东西,只是简单地生成了带有请求和响应的 JSON。我描述了应用程序前端的每个请求、向其发送请求的服务的预期响应,以及检查对主请求的响应的规则。
对于每种方法,我都编写了一个单独的 URL。
但是,手动将一种方法的响应从正确更改为不正确,反之亦然,然后拉动每种方法既困难又耗时。
JSON的
1 { 2 "id": 308, 3 "front": { 4 "method": "/method1", 5 "request": { 6 "method": "POST", 7 "data": { 8 "from_date": "dfsdsf", 9 "some_type": "dfsdsf", 10 "goods": [ 11 { 12 "price": "112323", 13 "name": "123123", 14 "quantity": 1 15 } 16 ], 17 "total_amount": "2113213" 18 } 19 }, 20 "response": { 21 "code": 200, 22 "body": { 23 "status": "OK", 24 "data": { 25 "uniq_id": "sdfsdfsdf", 26 "data": [ 27 { 28 "number": "12223", 29 "order_id": "12223", 30 "status": "active", 31 "code": "12223", 32 "url": "12223", 33 "op_id": "12223" 34 } 35 ] 36 } 37 } 38 } 39 }, 40 "backend": { 41 "response": { 42 "code": 200, 43 "method": "POST", 44 "data": { 45 "body": { 46 "status": 1, 47 "data": { 48 "uniq_id": "sdfsdfsdf", 49 "data": [ 50 { 51 "number": "12223", 52 "order_id": "12223", 53 "status": "active", 54 "code": "12223", 55 "url": "12223", 56 "op_id": "12223" 57 } 58 ] 59 } 60 } 61 } 62 } 63 } 64 }
第二种选择
然后,我将模拟对象链接到脚本。结果,似乎有一个脚本调用来拉取我的应用程序,并且有一个模拟对象响应其所有请求。脚本保存所选请求的 ID,mock 对象会根据此 ID 生成响应。
因此,我收集了不同选项中的所有请求:正确和错误。
我得到了什么
结果,我得到了一个简单的视图,所有 URL 都有一个函数。此函数采用特定的请求标识符,并基于该标识符查找响应规则 — 模拟对象。同时,在请求之前拉取服务的脚本会将此请求标识符写入存储。
这个脚本只是轮流处理每个案例,写入一个标识符,并发出正确的请求,然后检查响应是否正确,仅此而已。
中间连接
但是,我不仅需要生成对这些请求的响应,还需要测试模拟对象的请求。毕竟,该服务可能会发送不正确的请求,因此也有必要检查它们。结果,有大量的配置文件,我的几个 API 方法变成了数百个大型配置文件进行检查。
连接数据库
我决定将所有内容都转移到数据库中。
我的服务不仅开始写入控制台,还开始写入数据库,以便可以生成报告。这似乎更方便:每个案例在数据库中都有自己的条目。
案例被合并到项目中,并具有允许您禁用不相关选项的标志。在设置中,我添加了请求和响应修饰符,这些修饰符应应用于所有级别的每个请求和响应。
为了尽可能简化这一点,我使用了SQLite。Django 默认拥有它。我已将所有配置文件传输到数据库中,并将所有测试结果保存在其中。
算法
因此,我找到了一个非常简单灵活的解决方案。它已经可以作为三个微服务的外部集成测试,但我是唯一一个使用它的人。它当然不会覆盖单元测试,但它很好地补充了它们。当我需要验证服务时,我使用这个 Django 测试器来执行此操作。
配置文件示例
设置变得更简单,并使用 Django Admin 进行管理。我可以轻松地关闭它们、更改和观看历史记录。我可以走得更远,制作一个完整的 UI,但这对我来说已经绰绰有余了。
请求正文 JSON
JSON的
1 { 2 "from_date": "dfsdsf", 3 "some_type": "dfsdsf", 4 "goods": [ 5 { 6 "price": "112323", 7 "name": "123123", 8 "quantity": 1 9 } 10 ], 11 "total_amount": "2113213" 12 }
响应正文 JSON
JSON的
1 { 2 "uniq_id": "sdfsdfsdf", 3 "data": [ 4 { 5 "number": "12223", 6 "order_id": "12223", 7 "status": "active", 8 "code": "12223", 9 "url": "12223", 10 "op_id": "12223" 11 } 12 ] 13 }
后端响应正文 JSON
JSON的
1 { 2 "status": 1, 3 "data": { 4 "uniq_id": "sdfsdfsdf", 5 "data": [ 6 { 7 "number": "12223", 8 "order_id": "12223", 9 "status": "active", 10 "code": "12223", 11 "url": "12223", 12 "op_id": "12223" 13 } 14 ] 15 } 16 }
它给你什么
这项服务在哪些方面有用?有时,即使进行测试,也需要从外部拉取服务,或者从链中拉取多个服务。服务也可以是黑匣子。数据库可以在 Docker 中运行。至于API...API 也可以在 Docker 中运行。您需要设置主机、端口和配置文件并运行它。
为什么选择这种不寻常的解决方案?
有人可能会说,您可以使用第三方工具集成测试或其他一些测试。当然可以!但是,由于资源有限,通常没有时间应用所有这些,需要快速有效的解决方案。这里出现了满足所有要求的最简单的 Django 服务。