seldom 2.0 讓接口自動化測試更簡單


前言

HTTP接口測試很簡單,不管工具、框架、還是平台,只要很的好的幾個點就是好工具。

  1. 測試數據問題:比如刪除接口,重復執行還能保持結果一致,必定要做數據初始化。
  2. 接口依賴問題:B接口依賴A的返回值,C接口依賴B接口的返回值。
  3. 加密問題:不同的接口加密規則不一樣。有些用到時間戳、md5、base64、AES,如何提供種能力。
  4. 斷言問題:有些接口返回的結構體很復雜,如何靈活的做到斷言。

對於以上問題,工具和平台要么不支持,要么很麻煩,然而框架是最靈活的。

unittest/pytest + requests/https 直接上手寫代碼就好了,既簡單又靈活。

那么同樣是寫代碼,A框架需要10行,B框架只需要5行,然而又不失靈活性,那我當然是選擇更少的了,畢竟,人生苦短嘛。

seldom適合個人接口自動化項目,它有以下優勢。

  • 可以寫更少的代碼
  • 自動生成HTML/XML測試報告
  • 支持參數化,減少重復的代碼
  • 支持生成隨機數據
  • 支持har文件轉case
  • 支持數據庫操作

這些是seldom支持的功能,我們只需要集成HTTP接口庫,並提供強大的斷言即可。seldom 2.0 加入了HTTP接口自動化測試支持。

Seldom 兼容 Requests API 如下:

seldom requests
self.get() requests.get()
self.post() requests.post()
self.put() requests.put()
self.delete() requests.delete()

Seldom VS Request+unittest

先來看看unittest + requests是如何來做接口自動化的:

import unittest
import requests


class TestAPI(unittest.TestCase):

    def test_get_method(self):
        payload = {'key1': 'value1', 'key2': 'value2'}
        r = requests.get("http://httpbin.org/get", params=payload)
        self.assertEqual(r.status_code, 200)


if __name__ == '__main__':
    unittest.main()

這其實已經非常簡潔了。同樣的用例,用seldom實現。

# test_req.py
import seldom


class TestAPI(seldom.TestCase):

    def test_get_method(self):
        payload = {'key1': 'value1', 'key2': 'value2'}
        self.get("http://httpbin.org/get", params=payload)
        self.assertStatusCode(200)


if __name__ == '__main__':
    seldom.main()

主要簡化點在,接口的返回數據的處理。當然,seldom真正的優勢在斷言、日志和報告。

har to case

對於不熟悉 Requests 庫的人來說,通過Seldom來寫接口測試用例還是會有一點難度。於是,seldom提供了har 文件轉 case 的命令。

首先,打開fiddler 工具進行抓包,選中某一個請求。

然后,選擇菜單欄:file -> Export Sessions -> Selected Sessions...

選擇導出的文件格式。

點擊next 保存為demo.har 文件。

最后,通過seldom -h2c 轉為demo.py 腳本文件。

> seldom -h2c .\demo.har
.\demo.py
2021-06-14 18:05:50 [INFO] Start to generate testcase.
2021-06-14 18:05:50 [INFO] created file: D:\.\demo.py

demo.py 文件。

import seldom


class TestRequest(seldom.TestCase):

    def start(self):
        self.url = "http://httpbin.org/post"

    def test_case(self):
        headers = {"User-Agent": "python-requests/2.25.0", "Accept-Encoding": "gzip, deflate", "Accept": "application/json", "Connection": "keep-alive", "Host": "httpbin.org", "Content-Length": "36", "Origin": "http://httpbin.org", "Content-Type": "application/json", "Cookie": "lang=zh"}
        cookies = {"lang": "zh"}
        self.post(self.url, json={"key1": "value1", "key2": "value2"}, headers=headers, cookies=cookies)
        self.assertStatusCode(200)


if __name__ == '__main__':
    seldom.main()

運行測試

打開debug模式seldom.run(debug=True) 運行上面的用例。

> python .\test_req.py
2021-04-29 18:19:39 [INFO] A run the test in debug mode without generating HTML report!
2021-04-29 18:19:39 [INFO]
              __    __
   ________  / /___/ /___  ____ ____
  / ___/ _ \/ / __  / __ \/ __ ` ___/
 (__  )  __/ / /_/ / /_/ / / / / / /
/____/\___/_/\__,_/\____/_/ /_/ /_/
-----------------------------------------
                             @itest.info

test_get_method (test_req.TestAPI) ...
----------- Request 🚀 ---------------
url: http://httpbin.org/get         method: GET
----------- Response 🛬️ -------------
type: json
{'args': {'key1': 'value1', 'key2': 'value2'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a883c-7b355ba81fcd0d287566405a'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?key1=value1&key2=value2'}
ok

----------------------------------------------------------------------
Ran 1 test in 0.619s

OK

通過日志/報告都可以清楚的看到。

  • 請求的方法
  • 請求url
  • 響應的類型
  • 響應的數據

更強大的斷言

斷言接口返回的數據是我們在做接口自動化很重要的工作。

assertJSON

接口返回結果如下:

{
  "args": {
    "hobby": [
      "basketball",
      "swim"
    ],
    "name": "tom"
  }
}

我的目標是斷言namehobby 部分的內容。seldom可以針對JSON文件進行斷言。

import seldom


class TestAPI(seldom.TestCase):

    def test_assert_json(self):
        payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
        self.get("http://httpbin.org/get", params=payload)
        assert_json = {'args': {'hobby': ['swim', 'basketball'], 'name': 'tom'}}
        self.assertJSON(assert_json)

運行日志

test_get_method (test_req.TestAPI) ...
----------- Request 🚀 ---------------
url: http://httpbin.org/get         method: GET
----------- Response 🛬️ -------------
type: json
{'args': {'hobby': ['basketball', 'swim'], 'name': 'tom'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a896d-48fac4f6139912ba01d2626f'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?name=tom&hobby=basketball&hobby=swim'}
💡 Assert data has not key: headers
💡 Assert data has not key: origin
💡 Assert data has not key: url
ok

----------------------------------------------------------------------
Ran 1 test in 1.305s

OK

seldom還會提示你還有哪些字段沒有斷言。

assertPath

接口返回數據如下:

{
  "args": {
    "hobby": 
      ["basketball", "swim"], 
    "name": "tom"
  }
}

seldom中可以通過path進行斷言:

import seldom


class TestAPI(seldom.TestCase):

    def test_assert_path(self):
        payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
        self.get("http://httpbin.org/get", params=payload)
        self.assertPath("name", "tom")
        self.assertPath("args.hobby[0]", "basketball")

assertSchema

有時並不關心數據本身是什么,而是需要斷言數據的類型。 assertSchema 是基於 jsonschema 實現的斷言方法。

jsonschema: https://json-schema.org/learn/

接口返回數據如下:

{
  "args": {
    "hobby": 
      ["basketball", "swim"], 
    "name": "tom", 
    "age": "18"
  }
}

seldom中可以通過利用jsonschema 進行斷言:

import seldom


class TestAPI(seldom.TestCase):

    def test_assert_schema(self):
        payload = {"hobby": ["basketball", "swim"], "name": "tom", "age": "18"}
        self.get("/get", params=payload)
        schema = {
            "type": "object",
            "properties": {
                "args": {
                    "type": "object",
                    "properties": {
                        "age": {"type": "string"},
                        "name": {"type": "string"},
                        "hobby": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                        }
                    }
                }
            },
        }
        self.assertSchema(schema)

是否再次感受到了seldom提供的斷言非常靈活,強大。

接口數據依賴

在場景測試中,我們需要利用上一個接口的數據,調用下一個接口。

import seldom

class TestRespData(seldom.TestCase):

    def test_data_dependency(self):
        """
        Test for interface data dependencies
        """
        headers = {"X-Account-Fullname": "bugmaster"}
        self.get("/get", headers=headers)
        self.assertStatusCode(200)

        username = self.response["headers"]["X-Account-Fullname"]
        self.post("/post", data={'username': username})
        self.assertStatusCode(200)

seldom提供了self.response用於記錄上個接口返回的結果,直接拿來用即可。

數據驅動

seldom本來就提供的有強大的數據驅動,拿來做接口測試非常方便。

@data

import seldom
from seldom import data


class TestDDT(seldom.TestCase):

    @data([
        ("key1", 'value1'),
        ("key2", 'value2'),
        ("key3", 'value3')
    ])
    def test_data(self, key, value):
        """
        Data-Driver Tests
        """
        payload = {key: value}
        self.post("/post", data=payload)
        self.assertStatusCode(200)
        self.assertEqual(self.response["form"][key], value)

@file_data

創建data.json數據文件

{
 "login":  [
    ["admin", "admin123"],
    ["guest", "guest123"]
 ]
}

通過file_data實現數據驅動。

import seldom
from seldom import file_data


class TestDDT(seldom.TestCase):

    @file_data("data.json", key="login")
    def test_data(self, username, password):
        """
        Data-Driver Tests
        """
        payload = {username: password}
        self.post("http://httpbin.org/post", data=payload)
        self.assertStatusCode(200)
        self.assertEqual(self.response["form"][username], password)

更過數據文件(csv/excel/yaml),參考

隨機生成測試數據

seldom提供隨機生成測試數據方法,可以生成一些常用的數據。

import seldom
from seldom import testdata


class TestAPI(seldom.TestCase):

    def test_data(self):
        phone = testdata.get_phone()
        payload = {'phone': phone}
        self.get("http://httpbin.org/get", params=payload)
        self.assertPath("args.phone", phone)

更過類型的測試數據,參考

數據庫操作

seldom 支持sqlite3、MySQL數據庫操作。

sqlite3 MySQL
delete_data() delete_data()
insert_data() insert_data()
select_data() select_data()
update_data() update_data()
init_table() init_table()
close() close()

連接數據庫

連接sqlit3數據庫

from seldom.db_operation import SQLiteDB

db = SQLiteDB(r"D:\learnAPI\db.sqlite3")

連接MySQL數據庫(需要)

  1. 安裝pymysql驅動
> pip install pymysql
  1. 鏈接
from seldom.db_operation import MySQLDB

db = MySQLDB(host="127.0.0.1", 
             port="3306", 
             user="root", 
             password="123", 
             database="db_name")

操作方法

  • delete_data

刪除表數據。

db.delete_data(table="user", where={"id":1})
  • insert_data

插入一條數據。

data = {'id': 1, 'username': 'admin', 'password': "123"},
db.insert_data(table="user", data=data)
  • select_data

查詢表數據。

result = db.select_data(table="user", where={"id":1, "name": "tom"})
print(result)
  • update_data

更新表數據。

db.update_data(table="user", data={"name":"new tom"}, where={"name": "tom"})
  • init_table

批量插入數據,在插入之前先清空表數據。


datas = {
    'api_event': [
        {'id': 1, 'name': '紅米Pro發布會'},
        {'id': 2, 'name': '可參加人數為0'},
        {'id': 3, 'name': '當前狀態為0關閉'},
        {'id': 4, 'name': '發布會已結束'},
        {'id': 5, 'name': '小米5發布會'},
    ],
    'api_guest': [
        {'id': 1, 'real_name': 'alen'},
        {'id': 2, 'real_name': 'has sign'},
        {'id': 3, 'real_name': 'tom'},
    ]
}

db.init_table(datas)
  • close

關閉數據庫連接。

db.close()

最后,基於seldom 實現接口自動化測試的項目:https://github.com/defnngj/pyrequest2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM