HTTP接口测试很简单,不管工具、框架、还是平台,只要很的好的几个点就是好工具。
测试数据问题:比如删除接口,重复执行还能保持结果一致,必定要做数据初始化。接口依赖问题:B接口依赖A的返回值,C接口依赖B接口的返回值。加密问题:不同的接口加密规则不一样。有些用到时间戳、mdbase6AES,如何提供种能力。断言问题:有些接口返回的结构体很复杂,如何灵活的做到断言。
对于以上问题,工具和平台要么不支持,要么很麻烦,然而框架是最灵活的。
unittest/pytest+requests/https直接上手写代码就好了,既简单又灵活。
那么同样是写代码,A框架需要10行,B框架只需要5行,然而又不失灵活性,那我当然是选择更少的了,毕竟,人生苦短嘛。
seldom适合个人接口自动化项目,它有以下优势。
可以写更少的代码自动生成HTML/XML测试报告支持参数化,减少重复的代码支持生成随机数据支持har文件转case支持数据库操作
这些是seldom支持的功能,我们只需要集成HTTP接口库,并提供强大的断言即可。seldom0加入了HTTP接口自动化测试支持。
seldom | requests |
---|---|
self.get() | requests.get() |
self.post() | requests.post() |
self.put() | requests.put() |
self.delete() | requests.delete() |
先来看看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真正的优势在断言、日志和报告。
对于不熟悉Requests库的人来说,通过Seldom来写接口测试用例还是会有一点难度。于是,seldom提供了har文件转case的命令。
打开fiddler工具进行抓包,选中某一个请求。
然后,选择菜单栏:file->ExportSessions->SelectedSessions...
选择导出的文件格式。
点击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模式seldorun(debug=Tru运行上面的用例。
> python . est_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响应的类型响应的数据
更强大的断言
断言接口返回的数据是我们在做接口自动化很重要的工作。
接口返回结果如下:
{
'args': {
'hobby': [
'basketball',
'swim'
],
'name': 'tom'
}
}
我的目标是断言name和hobby部分的内容。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还会提示你还有哪些字段没有断言。
接口返回数据如下:
{
'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是基于jsonschema实现的断言方法。
接口返回数据如下:
{
'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提供了selresponse用于记录上个接口返回的结果,直接拿来用即可。
数据驱动
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)
创建datjson数据文件
{
'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/yam,参考
随机生成测试数据
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支持sqliteMySQL数据库操作。
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数据库
连接MySQL数据库
安装pymysql驱动
链接
from seldom.db_operation import MySQLDB
db = MySQLDB(host='127.0.0.1',
port='3306',
user='root',
password='123',
database='db_name')
操作方法
删除表数据。
db.delete_data(table='user', where={'id':1})
插入一条数据。
data = {'id': 1, 'username': 'admin', 'password': '123'},
db.insert_data(table='user', data=data)
查询表数据。
result = db.select_data(table='user', where={'id':1, 'name': 'tom'})
print(result)
更新表数据。
db.update_data(table='user', data={'name':'new tom'}, where={'name': 'tom'})
批量插入数据,在插入之前先清空表数据。
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)
关闭数据库连接。
db.close()
文章为作者独立观点,不代表股票交易接口观点