在接口自动化测试中,编码效率直接影响测试工作的进展和质量。为了在保证测试覆盖率的同时提高编码效率,我们需要采取一些有效的实践方法。
在之前的项目中我总结到了5大要点:断言方法封装、日志自动输出、参数化、接口请求方法封装以及变量管理。接下来我将围绕这5个要素详细说明如何通过它们来提升编码效率。
1. 断言方法封装
断言是接口自动化测试中不可或缺的一部分,用于验证接口返回结果是否符合预期。然而,直接在测试用例中编写大量的断言代码会导致代码冗余和维护成本增加。因此,封装常用的断言方法是提高编码效率的关键。
实现要点:
封装通用断言方法:将常用的断言逻辑(如状态码校验、返回体字段校验等)封装成通用的方法,供多个测试用例调用。
自定义断言方法:针对特定业务场景,封装自定义的断言方法,减少重复代码的编写。
断言方法的可扩展性:确保封装的断言方法具有良好的扩展性,能够根据业务需求灵活调整。
def code_assert(self, expect, actual):
# 对于返回状态码的校验
if expect != actual:
error_msg = f'res code different, expect code:{expect}, actual code:{actual}'
error('Assert failed!' + error_msg)
self.fail(error_msg)
def json_assert(self, expect, actual):
"""
json通用断言方法
:param expect: 定义预期返回体
:param actual: 实际返回的json
:return: 断言成功返回None,断言失败触发fail
"""
for key, value in expect.items():
# 校验字段是否存在
self.__assertIn(key,actual.keys())
# 校验返回体中是否存在多余字段
self.__assertEqual(len(expect.keys()), len(actual.keys()))
2. 日志的自动输出
日志是调试和排查问题的重要工具。在接口自动化测试中,自动输出详细的日志信息可以帮助我们快速定位问题,减少调试时间。
实现要点:
自动记录请求和响应:在每次接口请求后,自动记录请求的URL、请求参数、请求头以及响应状态码、响应体等信息。
日志分级管理:根据日志的重要性,设置不同的日志级别(如DEBUG、INFO、ERROR等),便于在不同场景下查看关键信息。
日志格式化:统一日志的输出格式,确保日志信息清晰易读。
以下为日志装饰器实现内容:
def case_decoration(func):
"""装饰器函数:可以自动的为每个测试用例输出case级别的log信息,
并且为测试用例添加 执行统计时间和超时检测"""
@functools.wraps(func) # 解决参数冲突问题
def case_improve(*args, **kwargs):
start = time.perf_counter() # 获取当前时间戳,记录测试用例执行的开始时间
class_name = args[0].__class__.__name__ # 获取类名
method_name = func.__name__ #获取方法的名称
docstring = inspect.getdoc(func) # 获取方法注释
case('-----------------------------------------------')
case(f'Method Name:{method_name}, Class Name:{class_name}')
case(f'Test Description:{docstring} ')
func(*args, **kwargs) # 执行测试用例
end = time.perf_counter()
handle_time = end - start
case('Case run time: %.2fs' % handle_time)
if handle_time > timeout:
error('case run timeout!')
return case_improve
def class_case_decoration(cls):
"""用例的日志类级别装饰器,用于自动为类中以 testCase 开头的方法应用 case_decoration 装饰器"""
# 获取类中所有的成员方法
for name, method in inspect.getmembers(cls, inspect.isfunction):
if name.startswith('testCase'):
# 只对以 testCase 开头 的方法进行装饰
setattr(cls, name, case_decoration(method))
return cls
3. 参数化
参数化是提高测试用例复用性和覆盖率的有效手段。通过参数化,我们可以使用不同的测试数据运行相同的测试逻辑,减少重复代码的编写。
实现要点:
数据驱动测试:将测试数据与测试逻辑分离,使用外部文件(如YAML等)存储测试数据,并在测试用例中动态读取。
动态生成测试数据:对于复杂的测试场景,可以通过代码动态生成测试数据,减少手动维护测试数据的工作量。
4. 接口请求方法封装
在接口自动化测试中,直接使用通用的请求方法可能会导致代码可读性差和维护成本高。为了提高编码效率,我们可以根据业务场景封装特定的接口请求方法。
实现要点:
业务场景封装:根据具体的业务场景,封装具有特定功能的接口请求方法,减少重复代码的编写。
提高代码可读性:通过封装,使接口请求代码更加简洁和易读,便于后续维护和扩展。
灵活调整:封装的请求方法应具备一定的灵活性,能够根据业务需求进行调整。
class BusinessRequest:
@staticmethod
def post(url, sid, user_id, body, headers=None):
if headers is None:
headers = { # 每次调用时生成新的默认 headers
'Cookie': f'sid={sid}',
'X-user-key': str(user_id),
'Content-Type':'application/json'
}
info(f'request url:{url}')
info(f'request headers:{headers}')
info(f'request body:{body}')
try:
res = requests.post(url, headers=headers, json=body, timeout=6)
except requests.exceptions.Timeout as e:
# 专门捕获请求超时异常
error(f'url:{url}, request timeout!')
return None # 抛出自定义异常
except requests.exceptions.RequestException as e:
# 捕获其他所有 requests 异常(如连接错误、HTTP 错误等)
error(f"Request failed: {str(e)}")
return None # 抛出自定义异常
info(f'response code:{res.status_code}')
info(f'response body:{res.text}')
return res #返回Response 对象
5. 变量管理
在接口自动化测试中,合理管理变量可以提高代码的可维护性和可读性。通过提取和分类变量,我们可以减少重复代码的编写,并提高测试用例的复用性。
实现要点:
用例变量提取:将测试用例中可复用的变量提取到类属性中,减少重复代码的编写。
环境变量管理:将环境相关的变量(如用户ID、身份信息、HOST、加密秘钥等)存储在环境变量的配置文件中,便于在不同环境中切换。
接口变量管理:将接口相关的变量(如接口路径、请求方式、必填参数、选填参数等)存储在接口配置文件中,便于统一管理。
apiConfig.yml
createMsg:
# 创建msg 接口信息
path: '/xx/xx/xxxx'
method: POST
mustKeys:
- 'msgId'
notMustKeys:
- 'msgContent'
envConfig.yml
host : 'https://xx/xxx/xxx'
userid: '123456'
cookie: 'xxxxxxxxxxx'
test_case.py
中的具体使用
class CreateMsg(unittest.TestCase):
env_config = YamlRead().env_config() # 读取配置文件
api_config = YamlRead().api_config()['createMsg']
host = env_config['host']
user_id = env_config['userid']
path = api_config['path']
url = host + path
def testCase01_major(self):
"""创建msg 主流程测试用例"""
headers = {
'cookie': f'{self.cookie}'
}
msg_id = str(int(time.time() * 1000))
body = {
"msgId": msg_id,
"content": "X22222222222",
}
expect_res = {
"responseTime": int,
}
res = requests.post(url=self.url, headers=headers, json=body)
变量管理的步骤:
用例中可复用的变量提炼在类属性上:将测试用例中重复使用的变量提取到类属性中,减少重复代码。
将类类型按环境和接口进行分类:根据环境和接口的不同,将变量分类管理。
将环境相关的类属性存储在环境变量的yaml配置文件当中:将环境相关的变量存储在YAML配置文件中,便于环境切换。
from main import DIR,ENVIRON
import yaml
class YamlRead:
"""yaml 配置文件的读取方法"""
@staticmethod
def env_config():
"""环境变量的读取"""
with open(file=f'{DIR}/data/envConfig/{ENVIRON}/envConfig.yml', mode='r',encoding='utf-8') as f:
return yaml.load(f, Loader = yaml.FullLoader)
@staticmethod
def api_config():
"""api变量的读取"""
with open(file=f'{DIR}/data/apiConfig/apiConfig.yml', mode='r',encoding='utf-8') as f:
return yaml.load(f, Loader = yaml.FullLoader)
实现yaml读取方法:编写YAML文件的读取方法,动态加载配置文件。
将接口相关的类属性存储在apiconfig配置文件中:将接口相关的变量存储在接口配置文件中,便于统一管理。
结论
通过断言方法封装、日志自动输出、参数化、接口请求方法封装以及变量管理等实践,我们可以显著提高接口自动化测试的编码效率。这些方法不仅减少了重复代码的编写,还提高了代码的可读性和可维护性,为自动化测试的顺利开展提供了有力保障。在实际项目中,根据具体业务需求灵活应用这些方法,将有助于进一步提升测试工作的效率和质量。