pytestx重新定义接口框架设计

概览

脚手架:

目录:

用例代码:

"""
测试登录到下单流程,需要先启动后端服务
"""

test_data = {
    "查询SKU": {
        "skuName": "电子书"
    },
    "添加购物车": {
        "skuId": 123,
        "skuNum": 2
    },
    "下单": {
        "orderId": 222,
        "payAmount": "0.2"
    },
    "支付": {
        "skuId": 123,
        "price": 0.1,
        "skuNum": 2,
        "totalPrice": 0.2
    },
}
case_vars = dict()


def test(http, login_headers, file_data):
    # 搜索商品
    url = file_data["domain"] + "/api/tasks/mock/searchSku"
    body = test_data["查询SKU"]

    response = http("get", url=url, headers=login_headers, params=body)
    assert response.status_code < 400

    case_vars["skuId"] = response.jsonpath("$.skuId")
    case_vars["skuPrice"] = response.jsonpath("$.price")

    # 添加购物车
    url = file_data["domain"] + "/api/tasks/mock/addCart"
    body = test_data["添加购物车"]
    body["skuId"] = case_vars["skuId"]

    response = http("post", url=url, headers=login_headers, json=body)
    assert response.status_code < 400

    case_vars["skuNum"] = response.jsonpath("$.skuNum")
    case_vars["totalPrice"] = response.jsonpath("$.totalPrice")

    # 下单
    url = file_data["domain"] + "/api/tasks/mock/order"
    body = test_data["下单"]
    body["skuId"] = case_vars["skuId"]
    body["price"] = case_vars["skuPrice"]
    body["skuNum"] = case_vars["skuNum"]
    body["totalPrice"] = case_vars["totalPrice"]

    response = http("post", url=url, headers=login_headers, json=body)
    assert response.status_code < 400

    case_vars["orderId"] = response.jsonpath("$.orderId")

    # 支付
    url = file_data["domain"] + "/api/tasks/mock/pay"
    body = test_data["支付"]
    body["orderId"] = case_vars["orderId"]

    response = http("post", url=url, headers=login_headers, json=body)
    assert response.status_code < 400
    assert response.jsonpath("$.success") == "true"

页面下载脚手架

启动平台前后端服务后,从页面下载脚手架,平台会拉取开源项目tep-project最新代码,打成压缩包,生成下载文件,弹窗下载。

备注:tep startproject demo使用的已封版的1.0.0版本,新框架请访问开源项目tep-project,或者开源平台pytestx

精简目录

目录直观上非常精简,得益于去掉了环境变量、函数等目录,聚焦三大目录:

  • fixtures

  • resources

  • tests

重度使用fixture

fixture原本只能在conftest定义,借助pytest插件扩展识别fixtures目录:

#!/usr/bin/python
# encoding=utf-8

"""
@Author  :  dongfanger
@Date    :  8/14/2020 9:16 AM
@Desc    :  插件
"""
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RESOURCE_PATH = os.path.join(BASE_DIR, "resources")


def fixture_paths():
    """
    fixture路径,1、项目下的fixtures;2、tep下的fixture;
    :return:
    """
    _fixtures_dir = os.path.join(BASE_DIR, "fixtures")
    paths = []
    # 项目下的fixtures
    for root, _, files in os.walk(_fixtures_dir):
        for file in files:
            if file.startswith("fixture_") and file.endswith(".py"):
                full_path = os.path.join(root, file)
                import_path = full_path.replace(_fixtures_dir, "").replace("\\", ".")
                import_path = import_path.replace("/", ".").replace(".py", "")
                paths.append("fixtures" + import_path)
    return paths


pytest_plugins = fixture_paths()  # +[其他插件]

conftest.py的fixture全部转移至fixtures目录定义。

公共函数消失,统统通过fixture来实现,依赖注入。

包括requests.request封装

#!/usr/bin/python
# encoding=utf-8

import decimal
import json
import time

import jsonpath
import pytest
import requests
import urllib3
from loguru import logger
from requests import Response

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class TepResponse(Response):
    """
    二次封装requests.Response,添加额外方法
    """

    def __init__(self, response):
        super().__init__()
        for k, v in response.__dict__.items():
            self.__dict__[k] = v

    def jsonpath(self, expr):
        """
        此处强制取第一个值,便于简单取值
        如果复杂取值,建议直接jsonpath原生用法
        """
        return jsonpath.jsonpath(self.json(), expr)[0]


@pytest.fixture(scope="session")
def http():
    def inner(method, url, **kwargs):
        template = """\n
    Request URL: {}
    Request Method: {}
    Request Headers: {}
    Request Payload: {}
    Status Code: {}
    Response: {}
    Elapsed: {}
    """
        start = time.process_time()
        response = requests.request(method, url, **kwargs)  # requests.request原生用法
        end = time.process_time()
        elapsed = str(decimal.Decimal("%.3f" % float(end - start))) + "s"
        headers = kwargs.get("headers", {})
        kwargs.pop("headers")
        payload = kwargs
        log = template.format(url, method, json.dumps(headers), json.dumps(payload), response.status_code,
                              response.text,
                              elapsed)
        logger.info(log)
        return TepResponse(response)

    return inner

只是名字换成了http:

http("post", url=url, headers=login_headers, json=body)

因为request是fixture保留关键字。

数据分离

数据支持从文件读取,当然这也是一个fixture:

import json
import os

import pytest
import yaml

from conftest import RESOURCE_PATH


class Resource:
    def __init__(self, path):
        self.path = path

    def get_data(self):
        file_type = self._get_file_type()
        if file_type in [".yml", ".yaml", ".YML", "YAML"]:
            return self._get_yaml_file_data()
        if file_type in [".json", ".JSON"]:
            return self._get_json_file_data()

    def _get_file_type(self):
        return os.path.splitext(self.path)[-1]

    def _get_yaml_file_data(self):
        with open(self.path, encoding="utf8") as f:
            return yaml.load(f.read(), Loader=yaml.FullLoader)

    def _get_json_file_data(self):
        with open(self.path, encoding="utf8") as f:
            return json.load(f)


@pytest.fixture(scope="session")
def file_data():
    file_path = os.path.join(RESOURCE_PATH, "demo.yaml")
    return Resource(file_path).get_data()

也可以放在用例文件中。为什么?“只改数据不动用例代码”,如果没有这种情况,请毫不犹豫将数据放到用例文件中,不要从excel、yaml读取数据,增加无意义的中间转换。从流量回放替代自动化的趋势来看,数据和用例作为整体来维护和运行,会越来越普遍。在使用低代码平台时,测试数据也是写在用例里面,只有少量的公共信息,会抽出来作为变量。测试技术在发展,只有符合当前实际使用需要的,才是最好的。

用例设计

约定大于配置:

  • 数据区域、用例区域分离

  • 用例由步骤组成

  • 步骤分为前置条件、用例体、断言、数据提取

数据区域,接口入参、用例中间变量等:

test_data = {
    "查询SKU": {
        "skuName": "电子书"
    },
    "添加购物车": {
        "skuId": 123,
        "skuNum": 2
    },
    "下单": {
        "orderId": 222,
        "payAmount": "0.2"
    },
    "支付": {
        "skuId": 123,
        "price": 0.1,
        "skuNum": 2,
        "totalPrice": 0.2
    },
}
case_vars = dict()

用例定义,test函数,fixture引用:

def test(http, login_headers, file_data):

步骤:

# 搜索商品
url = file_data["domain"] + "/api/tasks/mock/searchSku"
body = test_data["查询SKU"]

response = http("get", url=url, headers=login_headers, params=body)
assert response.status_code < 400

case_vars["skuId"] = response.jsonpath("$.skuId")
case_vars["skuPrice"] = response.jsonpath("$.price")

每个用例文件单独可运行。不存在用例依赖,复用步骤封装为fixture,以依赖注入方式在各用例中复用。用例一定要解耦,这在任务调度时非常重要。

总结,重新定义目录,重新定义用例组织,重新定义fixture,减少过程代码,专注于用例编写,轻松上手pytest。

跟着pytestx学习接口自动化框架设计,更简单,更稳定,更高效。

https://github.com/dongfanger/pytestx

https://gitee.com/dongfanger/tep-project

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/90428.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Matlab之智能优化算法函数调用

1.句柄函数 句柄函数即我们要求的目标函数&#xff0c;以下三种算法的调用仅是求解最小值&#xff0c;若要求目标函数的最大值&#xff0c;可在返回结果中加负号。 function value Get_Fitness(x,y)value x^2 y^2;% 若要求x^2 y^2最大值可设value -(x^2 y^2); end句柄函数…

跨境新手看过来!各国营销禁忌盘点!别再盲目踩雷了!

本土化是跨境卖家出海制胜的关键因素之一&#xff0c;不管是卖家的产品&#xff0c;还是营销推广策略&#xff0c;都要符合目标市场的习惯&#xff0c;才会有较好的效果。而与此相反的&#xff0c;如果卖家在营销过程中&#xff0c;踩到了营销雷区&#xff0c;那结果也可想而知…

机器视觉应用开发四大软件,那一个对我们从0到1建设你的机器视觉知识体系更好

我们首先要理解什么是知识体系&#xff1a; 我们身处一个知识爆炸时代&#xff0c;我们面对各种课程&#xff0c;各种知识&#xff0c;“自身学习”&#xff0c;“高效记忆”&#xff0c;“批判性思维”&#xff0c;“解决问题的能力”。各种平台课程太多&#xff0c;各种买买…

详解numpy.random.choice函数

文章目录 函数原型参数解析该函数的注意事项例子示例代码示例结果 参考 numpy的random模块中的choice函数用于从给定一维&#xff08;1-D&#xff09;数组中生成随机样本。本博客详细节将该函数的API&#xff0c;并给出示例代码和结果。 函数原型 numpy.random.choice(a, siz…

0825hw

//冒泡排序 void Bubble_sort(Sp p) {for(int i1;i<p->len;i){for(int j0;j<p->len-i;j){if(p->arr[j]>p->arr[j1]){int tp->arr[j];p->arr[j]p->arr[j1];p->arr[j1]t;}}} } //简单选择排序 void Simple_sort(Sp p) {for(int i1;i<p->l…

【android12-linux-5.1】【ST芯片】HAL移植后开机卡死

按照ST的官方readme移植HAL后开机一直卡在android界面&#xff0c;看logcat提示写文件时errorcode&#xff1a;-13。查下资料大致明白13错误码是权限不足&#xff0c;浏览代码在写文件的接口加日志后&#xff0c;发现是需要写iio:device*/buffer/enable这类文件的时候报错的。千…

UG\NX二次开发 使用录制功能录制操作记录时,如何设置默认的开发语言?

文章作者&#xff1a;里海 来源网站&#xff1a;王牌飞行员_里海_里海NX二次开发3000例,C\C,Qt-CSDN博客 简介&#xff1a; NX二次开发使用BlockUI设计对话框时&#xff0c;如何设置默认的代码语言&#xff1f; 效果&#xff1a; 方法&#xff1a; 依次打开“文件”->“实用…

K8s学习笔记3

Kubernetes功能&#xff1a; Kubernetes是一个轻便的可扩展的开源平台&#xff0c;用于管理容器化应用和服务。通过Kubernetes能够进行应用的自动化部署和扩缩容。在Kubernetes中&#xff0c;会将组成应用的容器组合成一个逻辑单元以更易管理和发现。Kubernetes积累了作为Goog…

npm yarn pnpm npx nvm 命令怎么区分怎么用

npm​​​​​​​ 包管理器&#xff0c;可以用来安装、卸载、更新和管理各种包npm的package.json中文文档 参数 - install&#xff1a;安装一个或多个包。例如&#xff1a;npm install 。 uninstall&#xff1a;卸载一个包。例如&#xff1a;npm uninstall 。 update&#xf…

Hadoop Hdfs基本命令

0目录 1.hadoop安装问题处理 2.hdfs基本命令 3.上传/下载文件和文件夹 1.hadoop安装问题处理 如果安装有进程无法启动&#xff0c;如下图 重新检查6个配置文件 Core-site.xml \ hdfs-site.xml \ hadoop-env.sh \ yarn-site.xml \ workers \ yarn-site.xml 来到hadoop313目录…

最新两年工作经验总结

最新两年工作经验总结 前言URP的使用1&#xff1a;如何开启URP1、老项目升级为URP2、创建新项目时选择URP创建 2&#xff1a;URP阴影的设置 PolyBrush的使用&#xff08;地图编辑插件&#xff09;制作山峰or低谷边缘柔化雨刷上色制造场景中的物体贴图地形创建容易踩坑的点ProBu…

10.Oracle中decode函数

【函数格式】&#xff1a; decode ( expression, condition_01, result_01, condition_02, result_02, ......, condition_n, result_n, result_default) 【函数说明】&#xff1a; 若表达式expression值与condition_01值匹配&#xff0c;则返回result_01&#xff0c;…

17.2.4 【Linux】通过systemctl分析各服务之间的相依性

使用list-dependencies时&#xff0c;所列出的default.target其实是muli-user.target的内容。multi-user.target 其实还会用到 basic.target getty.target remote-fs.target 三大项目&#xff0c; 而 basic.target 又用到了 sockets.target sysinit.target timers.target。 r…

2023年高教社杯 国赛数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

远程调试环境

一、远程调试 1.安装vscode 2.打开vscode&#xff0c;下载插件Remote-SSH,用于远程连接 3.安装php debug 4.远程连接&#xff0c;连接到远端服务器 注&#xff1a;连接远程成功后&#xff0c;在远程依然要进行安装xdebug&#xff0c;刚才只是在vscode中进行的安装。 5.配置la…

【Python编程】将同一种图片分类到同一文件夹下,并且将其分类的路径信息写成txt文件进行保存

注&#xff1a;数据结构同上一篇博文类似 一、代码 import os import cv2 import shutilpath0os.getcwd()\\apple\\RGB path1os.getcwd()\\apple\\tof_confidence # path2os.getcwd()\\apple\\tof_depth # path3os.getcwd()\\apple\\tof_depthRGB # path4os.getcwd()\\apple\…

多线程与高并发——并发编程(2)

文章目录 二、并发编程的三大特性1 原子性1.1 什么是原子性1.2 怎么保证原子性1.2.1 synchronized1.2.2 CAS1.2.3 Lock 锁1.2.4 ThreadLocal 2 可见性2.1 什么是可见性2.2 解决可见性的方式2.2.1 volatile2.2.2 synchronized2.2.3 Lock2.2.4 final 3 有序性3.1 什么是有序性3.2…

Rust常用加密算法

哈希运算(以Sha256为例) main.rs: use crypto::digest::Digest;use crypto::sha2::Sha256;fn main() { let input "dashen"; let mut sha Sha256::new(); sha.input_str(input); println!("{}", sha.result_str());} Cargo.toml: [package]n…

LeetCode 1162. As Far from Land as Possible【多源BFS】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

C++头文件和std命名空间

C 是在C语言的基础上开发的&#xff0c;早期的 C 还不完善&#xff0c;不支持命名空间&#xff0c;没有自己的编译器&#xff0c;而是将 C 代码翻译成C代码&#xff0c;再通过C编译器完成编译。 这个时候的 C 仍然在使用C语言的库&#xff0c;stdio.h、stdlib.h、string.h 等头…