response

上一篇说的client.py来发送请求,这里就来看另一个response.py,该文件主要是完成测试断言方法

可用资料

jmespath[json数据取值处理]: https://github.com/jmespath/jmespath.py

导包

from typing import Dict, Text, Any, NoReturn

import jmespath
import requests
from jmespath.exceptions import JMESPathError
from loguru import logger from httprunner import exceptions
from httprunner.exceptions import ValidationFailure, ParamsError
from httprunner.models import VariablesMapping, Validators, FunctionsMapping
# 数据解析,字符串解析,方法字典
from httprunner.parser import parse_data, parse_string_value, get_mapping_function

源码附注释

def get_uniform_comparator(comparator: Text):
""" convert comparator alias to uniform name
转换统一的比较器名称
"""
if comparator in ["eq", "equals", "equal"]:
return "equal"
elif comparator in ["lt", "less_than"]:
return "less_than"
elif comparator in ["le", "less_or_equals"]:
return "less_or_equals"
elif comparator in ["gt", "greater_than"]:
return "greater_than"
elif comparator in ["ge", "greater_or_equals"]:
return "greater_or_equals"
elif comparator in ["ne", "not_equal"]:
return "not_equal"
elif comparator in ["str_eq", "string_equals"]:
return "string_equals"
elif comparator in ["len_eq", "length_equal"]:
return "length_equal"
elif comparator in [
"len_gt",
"length_greater_than",
]:
return "length_greater_than"
elif comparator in [
"len_ge",
"length_greater_or_equals",
]:
return "length_greater_or_equals"
elif comparator in ["len_lt", "length_less_than"]:
return "length_less_than"
elif comparator in [
"len_le",
"length_less_or_equals",
]:
return "length_less_or_equals"
else:
return comparator def uniform_validator(validator):
""" unify validator
统一验证器
Args:
validator (dict): validator maybe in two formats: format1: this is kept for compatibility with the previous versions.
{"check": "status_code", "comparator": "eq", "expect": 201}
{"check": "$resp_body_success", "comparator": "eq", "expect": True}
format2: recommended new version, {assert: [check_item, expected_value]}
{'eq': ['status_code', 201]}
{'eq': ['$resp_body_success', True]} Returns
dict: validator info {
"check": "status_code",
"expect": 201,
"assert": "equals"
} """
if not isinstance(validator, dict):
raise ParamsError(f"invalid validator: {validator}") if "check" in validator and "expect" in validator:
# format1
check_item = validator["check"]
expect_value = validator["expect"]
message = validator.get("message", "")
comparator = validator.get("comparator", "eq") elif len(validator) == 1:
# format2
comparator = list(validator.keys())[0]
compare_values = validator[comparator] if not isinstance(compare_values, list) or len(compare_values) not in [2, 3]:
raise ParamsError(f"invalid validator: {validator}") check_item = compare_values[0]
expect_value = compare_values[1]
if len(compare_values) == 3:
message = compare_values[2]
else:
# len(compare_values) == 2
message = "" else:
raise ParamsError(f"invalid validator: {validator}") # uniform comparator, e.g. lt => less_than, eq => equals
assert_method = get_uniform_comparator(comparator) return {
"check": check_item,
"expect": expect_value,
"assert": assert_method,
"message": message,
} class ResponseObject(object):
def __init__(self, resp_obj: requests.Response):
""" initialize with a requests.Response object Args:
resp_obj (instance): requests.Response instance """
self.resp_obj = resp_obj
self.validation_results: Dict = {} def __getattr__(self, key):
# 魔术方法,查找属性时调用 实例对象.属性名
if key in ["json", "content", "body"]:
try:
value = self.resp_obj.json()
except ValueError:
value = self.resp_obj.content
elif key == "cookies":
value = self.resp_obj.cookies.get_dict()
else:
try:
value = getattr(self.resp_obj, key)
except AttributeError:
err_msg = "ResponseObject does not have attribute: {}".format(key)
logger.error(err_msg)
raise exceptions.ParamsError(err_msg) self.__dict__[key] = value
return value def _search_jmespath(self, expr: Text) -> Any:
# 根据jmespath语法搜索提取实际结果值
resp_obj_meta = {
"status_code": self.status_code,
"headers": self.headers,
"cookies": self.cookies,
"body": self.body,
}
if not expr.startswith(tuple(resp_obj_meta.keys())):
return expr try:
check_value = jmespath.search(expr, resp_obj_meta)
except JMESPathError as ex:
logger.error(
f"failed to search with jmespath\n"
f"expression: {expr}\n"
f"data: {resp_obj_meta}\n"
f"exception: {ex}"
)
raise return check_value def extract(self, extractors: Dict[Text, Text]) -> Dict[Text, Any]:
# 根据jmespath 语法找到值 放入 提取参数字典中
if not extractors:
return {} extract_mapping = {}
for key, field in extractors.items():
field_value = self._search_jmespath(field)
extract_mapping[key] = field_value logger.info(f"extract mapping: {extract_mapping}")
return extract_mapping # 验证&结果回写
def validate(
self,
validators: Validators,
variables_mapping: VariablesMapping = None,
functions_mapping: FunctionsMapping = None,
) -> NoReturn: variables_mapping = variables_mapping or {}
functions_mapping = functions_mapping or {} self.validation_results = {}
if not validators:
return validate_pass = True
failures = [] for v in validators: if "validate_extractor" not in self.validation_results:
self.validation_results["validate_extractor"] = [] u_validator = uniform_validator(v) # check item
check_item = u_validator["check"]
if "$" in check_item:
# 需要检查的元素是 变量或者函数
# check_item is variable or function
check_item = parse_data(
check_item, variables_mapping, functions_mapping
)
check_item = parse_string_value(check_item) if check_item and isinstance(check_item, Text):
check_value = self._search_jmespath(check_item)
else:
# variable or function evaluation result is "" or not text
check_value = check_item # comparator
assert_method = u_validator["assert"]
assert_func = get_mapping_function(assert_method, functions_mapping) # expect item
expect_item = u_validator["expect"]
# parse expected value with config/teststep/extracted variables
expect_value = parse_data(expect_item, variables_mapping, functions_mapping) # message
message = u_validator["message"]
# parse message with config/teststep/extracted variables
message = parse_data(message, variables_mapping, functions_mapping) validate_msg = f"assert {check_item} {assert_method} {expect_value}({type(expect_value).__name__})" validator_dict = {
"comparator": assert_method,
"check": check_item,
"check_value": check_value,
"expect": expect_item,
"expect_value": expect_value,
"message": message,
} try:
assert_func(check_value, expect_value, message)
validate_msg += "\t==> pass"
logger.info(validate_msg)
validator_dict["check_result"] = "pass"
except AssertionError as ex:
validate_pass = False
validator_dict["check_result"] = "fail"
validate_msg += "\t==> fail"
validate_msg += (
f"\n"
f"check_item: {check_item}\n"
f"check_value: {check_value}({type(check_value).__name__})\n"
f"assert_method: {assert_method}\n"
f"expect_value: {expect_value}({type(expect_value).__name__})"
)
message = str(ex)
if message:
validate_msg += f"\nmessage: {message}" logger.error(validate_msg)
failures.append(validate_msg) self.validation_results["validate_extractor"].append(validator_dict) if not validate_pass:
failures_string = "\n".join([failure for failure in failures])
raise ValidationFailure(failures_string)

HttpRunner3源码阅读:7.响应后处理 response.py的更多相关文章

  1. HttpRunner3源码阅读: 1. 目录结构分析

    初衷 身处软件测试行业的各位应该都有耳闻HttpRunner 开源测试工具/框架(接口测试),作者博客 为什么出这系列? 不少测试同行都建议阅读HttpRunner,源码学习其设计思想. 社区当下Py ...

  2. Django 源码小剖: 响应数据 response 的返回

    响应数据的返回 在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由 ...

  3. HttpRunner3源码阅读:2. 模型定义

    models.py 昨天体验的时候我们分别执行了httprunner -h,httprunner startproject demo, httprunner run demo,但是源码中其调用了其他文 ...

  4. HttpRunner3源码阅读:4. loader项目路径加载,用例文件转换、方法字典生成

    loader.py 这个文件中主要是对yaml,json用例加载转换成用例处理, 预置函数加载成方法字典,路径加载等 可用资料 [importlib]. https://docs.python.org ...

  5. httprunner3源码解读(2)models.py

    源码目录结构 我们首先来看下models.py的代码结构 我们可以看到这个模块中定义了12个属性和22个模型类,我们依次来看 属性源码分析 import os from enum import Enu ...

  6. httprunner3源码解读(3)client.py

    源码目录结构 ApiResponse 这个类没啥好说的 class ApiResponse(Response): """ 继承了requests模块中的Response类 ...

  7. Flask源码阅读-第二篇(flask\__init__.py)

    源码: # -*- coding: utf-8 -*-""" flask ~~~~~ A microframework based on Werkzeug. It's e ...

  8. httprunner3源码解读(4)parser.py

    源码结构目录 可以看到此模块定义了4个属性和12个函数,我们依次来讲解 属性源码分析 # 匹配http://或https:// absolute_http_url_regexp = re.compil ...

  9. HTTP请求库——axios源码阅读与分析

    概述 在前端开发过程中,我们经常会遇到需要发送异步请求的情况.而使用一个功能齐全,接口完善的HTTP请求库,能够在很大程度上减少我们的开发成本,提高我们的开发效率. axios是一个在近些年来非常火的 ...

随机推荐

  1. sql把一个字段中的特定字符替换成其他字符

    将'0654879'替换成'0754879' UPDATE dbo.SG_Functionality SET FunctionalityCode=REPLACE(FunctionalityCode,' ...

  2. Linux常用命令详解上

    Linux常用命令详解上 目录 一.shell 二.Linux命令 2.1.内部命令与外部命令的区别 2.2.Linux命令行的格式 2.3.编辑Linux命令行的辅助操作 2.4.获得命令帮助的方法 ...

  3. MySQL基本sql语句总结

    目录 约束 表操作 查看表结构与修改表名 修改字段名与字段数据类型 添加与删除字段 修改字段的排列位置 删除表的外键约束 增删改 插入数据 更新数据 删除数据 查询 单表查询 连接查询 子查询 视图 ...

  4. Nexus安装配置和使用

    Nexus安装配置和使用 第一步安装jdk yum install java-1.8.0-openjdk-devel 第二步下载nexus-3.12.1-01-unix.tar.gzjdk 下载地址: ...

  5. @EnableDiscoveryClient与Nacos自动注册

    前一阵看到有篇博客说cloud从Edgware版本开始,可以不加@EnableDiscoveryClient注解,只要配置好注册中心的相关配置即可自动开启服务注册功能,比较好奇其中的原理,研究了一番特 ...

  6. Spring Boot配置Filter

    此博客是学习Spring Boot过程中记录的,一来为了加深自己的理解,二来也希望这篇博客能帮到有需要的朋友.同时如果有错误,希望各位不吝指教 一.通过注入Bean的方式配置Filter: 注意:此方 ...

  7. keycloak~管理平台的查询bug与自定rest中文检索

    对于keycloak来说,它的管理平台在它的源码中的admin-client中,它会定义相关的rest接口规范:在我们使用keycloak管理平台时,其中有一个组的查询,在我们查询中文组时,它是不支持 ...

  8. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

  9. pybot参数

    p.p1 { margin: 0; font: 12px "Helvetica Neue" } p.p2 { margin: 0; font: 12px "Helveti ...

  10. MySQL服务器 IO 100%的分析与优化方案

    前言 压力测试过程中,如果因为资源使用瓶颈等问题引发最直接性能问题是业务交易响应时间偏大,TPS逐渐降低等.而问题定位分析通常情况下,最优先排查的是监控服务器资源利用率,例如先用TOP 或者nmon等 ...