本项目实现了对Daily Cost的接口测试:
- Python+Requests 发送和处理HTTP协议的请求接口
- Pytest 作为测试执行器
- YAML 管理测试数据
- Allure 来生成测试报告。
本项目是参考了pytestDemo做了自己的实现。
1. 项目结构及安装部署
项目结构
- api: 接口封装层,如封装HTTP接口为Python接口
- commom: 从文件中读取数据等各种工具类
- core: requests 请求方法封装、关键字返回结果类
- config: 环境、数据库连接等配置
- data: 测试数据文件管理
- operation: 关键字封装层,如把多个Python接口封装为关键字
- pytest.ini: pytest 配置文件
- requirements.txt: 相关依赖包文件
- testcases: 测试用例
- api_test: 单接口测试
- scenario_test: 场景/业务流程测试
安装部署
- 部署Daily Cost应用:教程
- 下载本测试项目源代码 ,通过pip安装相应依赖
pip3 install -r requirements.txt
,根据实际情况修改setting.ini中的相应内容 - 输入
pytest
运行测试 - 如果想要使用Allure查看生成的测试报告,需要先安装Allure服务:
brew install allure
2. 环境配置
- 在config-setting.ini文件中定义
api_root_url
- 在common-read_data.py中实现load_ini()方法读取配置文件
- 在需要使用
api_root_url
的地方调用load_ini(data_file_path)
,读取对应的value
3. 封装HTTP请求
- 封装requests请求方法:将requests中发送GET、POST、PUT、DELETE等方法封装到RestClient类中
- 定义被测API接口Request:在api文件夹中根据domain创建对应请求的集合类,如
class User(RestClient)
,并定义各接口信息,如登录接口:
def login(self, **kwargs):
return self.post("/login", **kwargs)
4. 关键字封装
关键字应当是具有一定业务意义的。在封装关键字的时候,可以只封装一个接口,也可以调用多个接口来完成。
比如我们要测试记一笔,在接口调用成功后接口只会返回账单的ID,还需要调用查询账单明细接口,来帮助判断每个字段是否与输入时的一致,那么我们可以这样来进行测试:
- 首先,将
记一笔-查看明细
的操作封装为一个关键字,在这个关键字中依次调用记一笔和查询账单明细结果,并可以自定义关键字的返回结果 - 接着,在编写测试用例的时候,直接调用关键字来进行测试,这时就可以关键字的返回结果,断言的时候,也可以直接对关键字返回结果进行断言
再比如查询月度账单统计结果,一个接口就可以独立完成业务查询操作,我们在关键字中只调用这一个接口即可。
回到本项目,具体的代码逻辑如下:
- 在operation文件夹中,还是按照domain分文件来组织各关键字
- 在core中定义关键字返回的数据结构类
ResultBase
- 定义关键字,在调用对应API请求时需要明确传递接口请求参数,并定义返回结果
def bill_monthly_stat(date, token):
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
param = {"date": date}
res = bill_details.get_bill_monthly(params=param, headers=header)
return ResultBase(res)
本项目中的关键字中都只封装了一个请求。
5. 单接口测试
以记一笔
接口为例。
- 定义接口及关键字
// api -> bill.py : 定义接口
def create_new_bill(self, **kwargs):
return self.post("/bill", **kwargs)
// operation -> bill.py : 定义关键字
def bill_create(category_id, type, amount, note, date, token):
payload = {
"categoryId": category_id,
"type": type,
"amount": amount,
"note": note,
"date": date
}
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
res = bill.create_new_bill(json=payload, headers=header)
return ResultBase(res)
- 接口测试类
// testcases -> api_test -> bill -> test_bill_create.py
class TestBillCreate():
@mark.smoke
@mark.parametrize('category_id, type, amount, note, date, status_code, message',
[
("5442d3b8-9d4a-4654-bf0b-d2249efef190", "EXPENSE", 100.01, "note1111test", "2021-12-01", 200, "操作成功"),
("66c22fad-be9d-481d-a445-c57d266bf938", "INCOME", 1000.01, "note1111test", "2021-12-01", 200, "操作成功")
])
def test_bill_create_success(self, category_id, type, amount, note, date, status_code, message, token):
result = bill_create(category_id, type, amount, note, date, token)
assert result.response.status_code == status_code
assert result.message == message
assert result.response.json()["data"]["id"]
@mark.smoke
是使用了pytest的mark标记功能,运行时使用命令pytest -m smoke
只运行标记为smoke的用例@mark.parametrize
是pytest的变量参数化功能,可以实现数据驱动测试,如上所示有两条参数化数据,则该用例会应用这两条数据共执行两次- 调用bill_create时有一个
token
参数,它实际上是在conftest.py
文件中定义的 - pytest中的assert使用的是python内置的断言库
6. YAML文件管理测试数据
在上面的例子中我们在使用@mark.parametrize
时直接将具体的测试数据写在了用例上方。为了后期修改维护效率更高,我们通常会将测试数据和用例分离,即可以采用yaml文件来单独管理测试数据,conftest.py可以帮助数据的读取。
- 定义yaml文件:data -> api_test_data.yml
test_bill_create_success:
# category_id, type, amount, note, date, status_code, message
- ["5442d3b8-9d4a-4654-bf0b-d2249efef190", "EXPENSE", 100.01, "note1111test", "2021-12-01", 200, "操作成功"]
- ["66c22fad-be9d-481d-a445-c57d266bf938", "INCOME", 1000.01, "note1111test", "2021-12-01", 200, "操作成功"]
key
为测试用例对应的函数名test_bill_create_success
-
表示value是数组conftest.py
中读取对应的测试数据
import pytest
import os
from common.read_data import data
BASE_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
def get_data(yaml_file_name):
try:
data_file_path = os.path.join(BASE_PATH, "data", yaml_file_name)
yaml_data = data.load_yaml(data_file_path)
except Exception as ex:
pytest.skip(str(ex))
else:
return yaml_data
api_data = get_data("api_test_data.yml")
- 参数化时从yaml文件中读取数据
@mark.parametrize('category_id, type, amount, note, date, status_code, message',
api_data["test_bill_create_success"])
7. API场景测试
在做API测试时,除了单个接口输入输出的校验,由多个接口串联成的业务场景的测试也是必不可少的。
简单的,我们以登录后进入首页为例,接口调用为:登录 -> 查询当月账单明细列表 -> 查询当月账单统计值。
- 定义场景测试类
// testcases -> scenario_test -> test_get_one_month_bill.py
import allure
import pytest
from operation.user import login_user
from operation.bill import one_month_bill_list_get_by_date, bill_monthly_stat
@pytest.mark.core
class TestGetOneMonthBill:
@allure.title("01: user[yuxiaomeng] login")
@allure.story('story_1')
def test_user_login(self, core_env):
result = login_user('yuxiaomeng', '20211030.y')
assert result.status_code == 200
assert result.data["token"]
core_env["token"] = result.data["token"]
@allure.title("02: get homepage info - bill details list")
@allure.story('story_1')
def test_get_current_month_monthly_bill_list(self, core_env):
result = one_month_bill_list_get_by_date(core_env["date"], core_env["token"])
assert result.status_code == 200
assert len(result.data) == 2
assert result.data[0]["date"] == "2021-11-11"
assert result.data[0]["expense"] == 510.5
@allure.title("03: get homepage info - bill monthly statistics ")
@allure.story('story_1')
def test_get_current_month_bill(self, core_env):
result = bill_monthly_stat(core_env["date"], core_env["token"])
assert result.status_code == 200
assert result.data["expense"]
- 其中
core_env
用于在接口间传递测试数据
// testcases -> scenario_test -> conftest.py
import pytest
@pytest.fixture(scope='session')
def core_env():
return {"date": "2021-11"}
8. Allure生成测试报告
allure-pytest的详细使用方法可以参考官方文档。在这里只做简单介绍。
- 运行测试时加上--alluredir参数可以生存报告到指定的文件夹:
pytest --alluredir=/tmp/my_allure_results
- 运行完后查看报告:
allure serve /tmp/my_allure_results
END配套学习资源分享
最后: 为了回馈铁杆粉丝们,我给大家整理了完整的软件测试视频学习教程,朋友们如果需要可以自行免费领取 【保证100%免费】
软件测试面试文档
我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
全套资料获取方式: