Pyresttest 自动测试 API 接口

发布于 2021-05-11 18:46:43 字数 13919 浏览 1855 评论 0

pyresttest 是一个用 python 实现的 API 自动化测试工具。使用 YMAL/JSON 格式的配置文件驱动,不用写代码。

安装 Pyresttest

$ apt-get install python-pycurl
$ pip install pyresttest

centos7 下安装:

$ yum install python-pycurl
$ yum -y install epel-release
$ yum install python-pip
$ pip install pyresttest

安装测试 REST 服务器

Pyresttest 库自带了测试用的 REST 服务器。

cd /opt
git clone https://github.com/svanoort/pyresttest.git
pip install 'django >=1.6, <1.7' django-tastypie==0.12.1
cd pyresttest/pyresttest/testapp
python manage.py testserver test_data.json &

如果运行成功,进程会监听 8000 端口。如果不加 & 符号,就需要另外再启动一个终端访问 block2 虚拟机。用下面的命令测试一下刚启动的 REST 服务:

curl -s http://localhost:8000/api/person/2/ | python -m json.tool

如果执行成功会返回响应:

{
    "first_name": "Leeroy",
    "id": 2,
    "last_name": "Jenkins",
    "login": "jenkins",
    "resource_uri": "/api/person/2/"
}

配置文件的基本语法

配置文件使用 YAML 格式。
有 5 个顶级的测试语法元素:

  • url: 简单测试,用给定的 GET 请求来检测响应状态码是否正确。
  • test: 完整测试定义。
  • benchmark: 定义基准测试。
  • config或configuration: 全部测试设置(超时是最常见选项)
  • import: 导入其它配置文件

配置文件范例

一个典型的 PyRestTest 配置文件:

---
- config:
    - testset: "Basic tests"
    - timeout: 100  # Increase timeout from the default 10 seconds
- test: 
    - name: "Basic get"
    - url: "/api/person/"
- test: 
    - name: "Get single person"
    - url: "/api/person/1/"
- test: 
    - name: "Delete a single person, verify that works"
    - url: "/api/person/1/"
    - method: 'DELETE'
- test: # create entity by PUT
    - name: "Create/update person"
    - url: "/api/person/1/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 1,"last_name": "Baltar","login": "gbaltar"}'
    - headers: {'Content-Type': 'application/json'}
    - validators:  # This is how we do more complex testing!
        - compare: {header: content-type, comparator: contains, expected:'json'}
        - compare: {jsonpath_mini: 'login', expected: 'gbaltar'}  # JSON extraction
        - compare: {raw_body:"", comparator:contains, expected: 'Baltar' }  # Tests on raw response
- test: # create entity by POST
    - name: "Create person"
    - url: "/api/person/"
    - method: "POST"
    - body: '{"first_name": "William","last_name": "Adama","login": "theadmiral"}'
    - headers: {Content-Type: application/json}

大致的解释:每个 - test 代表一个测试,会发起一次 REST 请求;紧跟的对象往往是定义请求的内容;validators 定义更复杂的验证逻辑,否则仅能通过响应状态码来确定测试是否通过;expected 定义预期值;jsonpath_mini 是提取语法用于从响应 json 串中提取值。

测试 1:首个冒烟测试

在 /opt/pyresttest/pyresttes 目录下建立 test.yaml 文件,文件内容:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - name: "Basic smoketest"
    - url: "/api/people/"

然后执行:

resttest.py http://localhost:8000 test.yaml

发现报错,提示 测试组 Default 失败。改正错误:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - group: "Quickstart"
    - name: "Basic smoketest"
    - url: "/api/person/"

执行成功!

测试 2:功能测试 - 创建/更新/删除

创建 test2.yaml:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - group: "Quickstart"
    - name: "Basic smoketest"
    - url: "/api/person/"

- test:
    - group: "Quickstart"
    - name: "Create a person"
    - url: "/api/person/10/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 10,"last_name": "Baltar","login": "baltarg"}'

发现报错,这是因为没有指定 Content-Type 导致的。修复的代码如下:

- test:
    - group: "Quickstart"
    - name: "Create a person"
    - url: "/api/person/10/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 10,"last_name": "Baltar","login": "baltarg"}'
    - headers: {'Content-Type': 'application/json'}

测试通过。然后增加一个测试检测刚刚添加的 10 号 Baltar 用户:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - group: "Quickstart"
    - name: "Basic smoketest"
    - url: "/api/person/"

- test:
    - group: "Quickstart"
    - name: "Create a person"
    - url: "/api/person/10/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 10,"last_name": "Baltar","login": "baltarg"}'
    - headers: {'Content-Type': 'application/json'}

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar was added"
    - url: "/api/person/10/"

如果在最开始 希望 10号用户不存在:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar ISN'T there to begin with"
    - url: "/api/person/10/"
    - expected_status: [404]

- test:
    - group: "Quickstart"
    - name: "Basic smoketest"
    - url: "/api/person/"

- test:
    - group: "Quickstart"
    - name: "Create a person"
    - url: "/api/person/10/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 10,"last_name": "Baltar","login": "baltarg"}'
    - headers: {'Content-Type': 'application/json'}

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar is there after we added him"
    - url: "/api/person/10/"

expected_status 表示预期状态是 404(表示该URL不存在),但 10 号用户 Baltar 已经存在,所以第一个测试不通过(Error)。 进一步改进测试代码,在最后增加删除 10 号用户 Baltar 的测试:

---
- config:
    - testset: "Quickstart app tests"

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar ISN'T there to begin with"
    - url: "/api/person/10/"
    - expected_status: [404]

- test:
    - group: "Quickstart"
    - name: "Basic smoketest"
    - url: "/api/person/"

- test:
    - group: "Quickstart"
    - name: "Create a person"
    - url: "/api/person/10/"
    - method: "PUT"
    - body: '{"first_name": "Gaius","id": 10,"last_name": "Baltar","login": "baltarg"}'
    - headers: {'Content-Type': 'application/json'}

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar is there after we added him"
    - url: "/api/person/10/"

- test:
    - group: "Quickstart"
    - name: "Get rid of Gaius Baltar!"
    - url: "/api/person/10/"
    - method: 'DELETE'

- test:
    - group: "Quickstart"
    - name: "Make sure Mr Baltar ISN'T there after we deleted him"
    - url: "/api/person/10/"
    - expected_status: [404]

现在6个测试全部通过了。这基本上是一个完整的测试,包括创建、查询、删除用户。

高阶向导

PyRestTest 可用于基准测试,而基准测试往往要运行多次来取平均值。PyRestTest 提供 Generators 来产生数据。生成器 Generator 和模板本次测试忽略。

提取器:jsonpath_mini

响应的例子:

{
    "thing":{"foo":"bar"},
    "link_ids": [1, 2, 3, 4],
    "person":{
        "firstname": "Bob",
        "lastname": "Smith",
        "age": 17
    }
}

在上述响应下使用提取器 jsonpath_mini:

  • jsonpath_mini: 'person.lastname' 返回 "Smith"
  • jsonpath_mini: 'person.is_a_ninja' 返回 NOTHING (None object) ;因为person对象下没有这个key
  • jsonpath_mini: 'link_ids.1' 返回 2
  • jsonpath_mini: 'thing' 返回 {"foo":"bar"}
  • jsonpath_mini: '.'返回整个响应
  • jsonpath_mini: 'thing.0' 返回 None;因为thing不是数组

提取器:header

从响应头中提取数据。不区分大小写。如果响应头中存在多个值(如cookie)则返回列表。 例子:

header: 'content-type'

例子2:

compare: {header: 'content-type', expected: 'application/json'}

提取器:raw_body

返回整个响应体。

验证器:extract_test

例子:

- validators:
    # Test key does not exist
    - extract_test: {jsonpath_mini: "key_should_not_exist",  test: "not_exists"}

检查值是否存在。

验证器:compare

例子:

- validators:
     # Check the user name matches
     - compare: {jsonpath_mini: "user_name", comparator: "eq", expected: 'neo'}

     # Check the total_count key has value over 10
     - compare: {jsonpath_mini: "total_count", comparator: "gt", expected: 10}

     # Check the user's login
     - compare: {jsonpath_mini: "total_count", comparator: "gt", expected: }

参数有3个:提取器、比较函数、预期值。

预期值也可以用提取器,这时可用于比较响应中包含的两个值。

比较函数清单:

Name(s)DescriptionDetails for comparator(A, B)
'count_eq','length_eq'Check length of body/str or count of elements equals valueength(A) == B or -1 if cannot obtain length
'lt', 'less_than':Less ThanA < B
'le', 'less_than_or_equal'Less Than Or Equal ToA <= B
'eq', 'equals'EqualsA == B
'str_eq'Values are Equal When Converted to Stringstr(A) == str(B) -- useful for comparing templated numbers/collections
'ne', 'not_equals'Not EqualsA != B
'ge', 'greater_than_or_equal'Greater Than Or Equal ToA >= B
'gt', 'greater_than'Greater ThanA > B
'contains'ContainsB in A
'contained_by'Contained ByA in B
'type'Type of variable isA instanceof (at least one of) B
'regex'Regex EqualsA matches regex B

json-server 与 httpbin.org

如果测试 pyresttest 需要一个 REST 模拟服务器。可以自己部署 json-server 或直接使用云服务 httpbin.org

webdav.imaicloud.com 测试

先参照 WebDav 测试。 创建名为 webdav.yaml 的配置文件:

---
- config:
    - testset: "webdav.imaicloud.com tests"

- test:
    - name: "Basic GET"
    - url: "/"

- test:
    - name: "upload a file"
    - url: "/pyresttest/uploadfile.txt"
    - method: "PUT"
    - body: "first line\nsecond line"

- test:
    - name: "get uploadfile.txt"
    - url: "/pyresttest/uploadfile.txt"

- test:
    - name: "delete uploadfile.txt"
    - url: "/pyresttest/uploadfile.txt"
    - method: "DELETE"

- test:
    - name: "delete directory pyresttest"
    - url: "/pyresttest/"
    - method: "DELETE"

执行:

$ pyresttest http://webdav.imaicloud.com webdav.yaml
Test Group Default SUCCEEDED: : 5/5 Tests Passed!

基准测试

下面这个配置文件用于对 composer-rest-server 进行基准测试:

---
- config:
    - testset: "Benchmark tests using Retails"
    - timeout: 100  # Increase timeout from the default 10 seconds
    - generators:
        # Generator named 'id' that counts up from 10
        - 'id': {type: 'number_sequence', start: 100}
- benchmark:
    - name: "Post Retailers"
    - url: "/api/Retailer"
    - generator_binds: {retailerId: id}
    - warmup_runs: 0
    - benchmark_runs: '10'
    - method: 'POST'
    - headers: {'Accept': 'application/json'}
    - headers: {'Content-Type': 'application/json'}
    - body: {template: '{
   "$class": "composer.food.supply.Retailer",
   "retailerId": "$retailerId",
   "products": [
     {
       "$class": "composer.food.supply.Product",
       "productId": "string",
       "quantity": "string",
       "countryId": "string",
       "id": "string"
     }
   ],
   "firstName": "string",
   "lastName": "string",
   "middleName": "string",
   "contactDetails": {
     "$class": "composer.base.ContactDetails",
     "email": "string",
     "mobilePhone": "string",
     "office": "string",
     "address": {
       "$class": "composer.base.Address",
       "city": "string",
       "country": "string",
       "locality": "string",
       "region": "string",
       "street": "string",
       "street2": "string",
       "street3": "string",
       "postalCode": "string",
       "postOfficeBoxNumber": "string",
       "id": "string"
     },
     "id": "string"
   }
 }' }
    - output_format: csv
    - output_file: 'food-supply-benchmark.csv'
    - metrics:
        - total_time: total
        - total_time: mean

测试报告 food-supply-benchmark.csv:

Benchmark,Post Retailers
Benchmark Group,Default
Failures,0
Aggregates,
total_time,total,30.886773999999996
total_time,mean,3.0886773999999995

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文