1.代码即用例(case的可读性和易用性) - Be5yond/pytest_demo GitHub Wiki
分层模块化组织自动化测试的实践demo
比如我们需要测试一个系统,🐬海豹系统(seal),对接一个审核平台🦛河马系统(hippo),两个系统分别有自己的前端界面和数据库,系统之间使用MQ进行通信。
我们需要对海豹的服务进行api自动化测试,按照分层设计自动化的代码,结构如下。
/lib/
Req.py ④
db.py ④
seal.py③
hippo.py③
/cases/
module_granularity/
conftest.py②
test_demo.py①
文件名后面的数字就是它的功能所出的层级,下面结合实际的代码来解释每一层都干了什么以及这么做的好处。
1.用例层
/cases/module_granularity/test_demo.py
使用模块和接口拼装业务场景完成测试
示例中的代码如下,只看测试方法中的内容
def test_audit_pass(self, user, auditor, data, scm):
with allure.step('1.提交工单'):
user.commit_order(data[0])
user.stash('json.type', 'order_id')
with allure.step('2.审核通过'):
auditor.approve(data[1])
with allure.step('3.验证工单状态为已通过'):
user.get_order_detail(data[2])
user.validate_resp(scm)
通过代码看出,这条用例的测试场景是3步,user提交一个工单,auditor对工单审核通过,然后user查询并校验工单状态验证审核结果。
通过模块化的封装实现代码即是用例,提高了case的😀可读性。
封装成和业务相关的关键字,也提高了case的编写效率,即😀易用性。
快速的组合user.commit_order -> auditor.reject - > user.get_order_detail即可完成一条工单被驳回的场景的自动化case。
2.模块层
/cases/module_granularity/conftest.py
分别对Seal类和Hippo类创建一个实例,user和auditor,对被测系统进行请求。 将一些接口操作组合成工作流,封装fixture,实现通用的业务步骤,比如创建一个create_valid_order,后续的测试方法中直接引用这个fixture,将testcase中的内容突出要测试的功能。
@pytest.fixture(scope='class')
def user(request):
"""Seal系统实例化一个user
Returns:
Session: a request Session
"""
env = request.config.getoption("--env")
ression = Seal(env=env)
return ression
...
@allure.step('创建一个审核通过的工单')
@pytest.fixture(scope='class')
def create_valid_order(user, auditor):
user.commit_order()
order_id = user.jsan().json.type()
auditor.approve({'order_id': order_id})
user.cache['order_id'] = order_id
3.接口层
/lib/seal.py
/lib/hippo.py创建Seal和Hippo两个类,方法实现和系统http接口的调用,有些测试需要操作数据库也可以放到这个文件里。 系统内通用数据的处理,Session级别和特定接口级别
比如:Seal系统中每个请求要有时间戳字段,或者要把请求的数据进行加密
def _get(self, path, para):
"""
send get request
Params:
| para | query args dict |
Return: response object
"""
para = self._add_common_params(para) # 每个get请求都默认添加了timestamp和trace_id字段
self.response = self.send('GET', self._host+path, para=para)
return self.response
def _add_common_params(self, args):
trace_id = str(uuid.uuid4())
self.cache['trace_id'] = trace_id
args.update({'traceId': trace_id,
'timestamp': int(time.time()*1000),
})
return args
某些接口有自己的必填参数但是和我的业务测试并不强相关,可以放到这里的接口上自动加上去,使testdata更加专注于业务,且减少无意义的testdata的维护。
比如Seal系统中query_stats接口有4个参数[StartTime, EndTime, Type, Animal],测试需要查询过去一个小时内动物的统计信息,需要改变的只有animal的种类。
比起维护一个
[{'StartTime': '{% timestr(minutes=-65) %}',
'EndTime': '{% timestr() %}',
'Type': 'default',
'Animal': 'cat'},
{'StartTime': '{% timestr(minutes=-65) %}',
'EndTime': '{% timestr() %}',
'Type': 'default',
'Animal': 'dot'},
{'StartTime': '{% timestr(minutes=-65) %}',
'EndTime': '{% timestr() %}',
'Type': 'default',
'Animal': 'horse'}.
...
]
这样的数据,更希望testdata只关注业务相关的字段的数据
使用DefaultData将默认参数加到接口上,testdata只需要维护animal字段的数据即可。 参数中的'{% %}'字符串解释见数据渲染与校验。
@DefaultData(data={ # 调用query_stats函数时data中会默认添加StartTime,EndTime,Type字段。
"StartTime": "{% timestr(minutes=-65) %}", # DefaultData装饰器代码实现在/lib/Req.py中
"EndTime": "{% timestr() %}",
"Type": "default"
})
def query_stats(self, data):
self._post('/post', data)
# testdata
querydata = [{'Animal': 'dog'},{'Animal': 'cat'},{'Animal': 'horse'}]
4.会话层
/lib/Req.py
实现http session的建立 上下文数据的缓存cache 结果校验函数的实现。 详细见数据渲染与校验
/lib/db.py(本例中没有这个文件😁)
实现对数据库的连接