接口自动化

news2024/11/16 8:36:13

为了实现真正意义上的接口自动化,一般使用yaml文件存储测试用例,代码调用里面的数据来发送请求


@Controller
@RequestMapping("/send")
public class Login {
    @ResponseBody
    @RequestMapping("/login")
    public State login(String name,String password) {
        if ("123".equals(name)&&"123".equals(password)) {
            State state = new State();
            state.setState(200);
            return state;
        }
        State state = new State();
        state.setState(101);
        return state;
    }
}

1、实现ymal文件的读取,并发送get请求

创建data.xml文件

-  name: 登录接口
   base_url: http://localhost:8080/send/login
   request:
     method: get
     params: name=123&password=123
   validate:
     - equals: [ "status_code",200 ]

读取内容,并发送请求

import requests
import yaml
from selenium.webdriver.chrome import webdriver
from selenium.webdriver.common.by import By

import common.ymal


class TestCase:
    def read_yaml(self,path):
        # 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
        with open(path, mode='r', encoding='utf-8') as f:
            # 读取f文件流, 读取方式FullLoader,然后赋值给value
            value = yaml.load(stream=f, Loader=yaml.FullLoader)
            return value
    def test_login(self):
      requests_val=self.read_yaml("test_package/data.yml")
      base_url=requests_val[0]["base_url"]
      method=requests_val[0]["request"]["method"]
      params=requests_val[0]["request"]["params"]
      #发送请求
      res=requests.session().request(method=method,url=base_url,params=params)
      assert res.json()["state"]==200

method属性表示请求发送的类型

params:表示get请求传递的数据

json:表示json格式传递的数据

data:表示以form表单格式传递的数据

2、发送post请求:设置请求头的数据类型,使用对应方式传参

class TestCase:
    def read_yaml(self,path):
        # 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
        with open(path, mode='r', encoding='utf-8') as f:
            # 读取f文件流, 读取方式FullLoader,然后赋值给value
            value = yaml.load(stream=f, Loader=yaml.FullLoader)
            return value
    def test_login(self):
      requests_val=self.read_yaml("test_package/data.yml")
      base_url=requests_val[0]["base_url"]
      method=requests_val[0]["request"]["method"]
      data=requests_val[0]["request"]["data"]
      hearder=requests_val[0]["request"]["hearders"]
      res=requests.session().request(method=method,url=base_url,data=data,headers=hearder)
      assert res.json()["state"]==200

3、实现文件上传


@Controller
@RequestMapping("/send")
public class upload {
    @PostMapping("/upload")
    public void Upload(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws IOException {
        String name = file.getOriginalFilename();
        response.setStatus(200);
        File newfile = new File("C:\\Users\\30283\\Desktop\\novel\\photo\\" ,name);
        file.transferTo(newfile);
    }
}

一般需要设置Content-Type: multipart/form-data,但是文件上传时会自动添加,如果自己添加了可能会报错

以files关键字来传递文件数据

 
-  name: 文件上传接口
   base_url: http://localhost:8080/send/upload
   request:
     method: post
     files:
       C:\\Users\\30283\Pictures\\picture\\七七.png
   validate:
     - equals: [ "status_code",200 ]
class TestCase:
    def read_yaml(self,path):
        # 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
        with open(path, mode='r', encoding='utf-8') as f:
            # 读取f文件流, 读取方式FullLoader,然后赋值给value
            value = yaml.load(stream=f, Loader=yaml.FullLoader)
            return value
    def test_login(self):
      requests_val=self.read_yaml("test_package/data.yml")
      base_url=requests_val[0]["base_url"]
      method=requests_val[0]["request"]["method"]
      file_path=requests_val[0]["request"]["files"]
      file_data = {'file': ("七七.png", open(file_path, 'rb'), 'image/png')}
      #以二进制流的形式打开文件
      requests.session().request(method=method,url=base_url,files=file_data)

把目标文件以open打开,然后存储到变量,并且使用files参数指明请求的参数名称、上传文件的类型、以及上传文件的路径。

4、实现接口关联

比如淘宝:只有用户登录之后,才可以加入商品到购物车,这时就要设置接口关联,使用新的ymal文件存储关联数据


@Controller
@RequestMapping("/set")
public class SetCookie {
    @RequestMapping("/setcookie")
    @ResponseBody
    public String set(HttpServletRequest request,HttpServletResponse response) {
        return "{\"token\":\""+UUID.randomUUID()+"\"}";
    }
}

封装ymal文件操作方法

# 文件说明:yaml文件的读取与写入
import yaml

#读取ymal文件的信息
def read_yaml(path):
    # 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
    with open(path, mode='r', encoding='utf-8') as f:
        # 读取f文件流, 读取方式FullLoader,然后赋值给value
        value = yaml.load(stream=f, Loader=yaml.FullLoader)
        return value

#给yaml文件追加信息
#为什么追加:让这个文件可以存储多个关联信息
def write_yaml(path, data):
    with open(path, mode='a', encoding='utf-8') as f:
        # 写入的数据从data传入
        yaml.dump(data=data, stream=f,allow_unicode=True)  #allow_unicode=True支持中文

#将这个clear_yaml配置到conftest中,这样可以清空之前的关联信息
# 清空yaml文件内容
def clear_yaml(path):
    # 打开yaml文件
        with open(path, mode='w', encoding='utf-8') as f:
            # 清空yaml文件
          f.truncate()


#读取文件的某一个数据
def read_value(path,key):
    # 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
    with open(path, mode='r', encoding='utf-8') as f:
        # 读取f文件流, 读取方式FullLoader,然后赋值给value
        value = yaml.load(stream=f, Loader=yaml.FullLoader)
        return value[key]

在conftest.py文件中,让clear_yaml()方法设置自动使用

@pytest.fixture(scope="session",autouse=True)
def clear():
    common.ymal.clear_yaml("C:/python学习/python自动化测试/temp.ymal")

class TestCase:
    def test_login(self):
      requests_val=  common.ymal.read_yaml("test_package/data.yml")
      base_url=requests_val[0]["base_url"]
      method=requests_val[0]["request"]["method"]
      res=requests.session().request(url=base_url,method=method)
      #将返回的中间数据存储到ymal文件中
      common.ymal.write_yaml("temp.ymal", res.json())

    def test_check(self):
        print(common.ymal.read_value("temp.ymal","token"))

token保留在中间文件中,并且每次保留的都是最新值

5、可以使用extract关键字提取接口的返回结果

@Controller
@RequestMapping("/set")
public class SetCookie {
    @RequestMapping("/setcookie")
    @ResponseBody
    public String set(HttpServletRequest request,HttpServletResponse response) {
        HttpSession session=request.getSession(true);
        UUID val=UUID.randomUUID();
        session.setAttribute("token",val);
        return "{\"token\":\""+val+"\"}";
    }
}

@Controller
@RequestMapping("/get")
public class GetCookie {
    @ResponseBody
    @RequestMapping("/getcookie")
    public String get(String token,HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("token") != null)
            return "ok";
        return "error";
    }
}

1、yaml文件新建extract参数,里面填写需要从当前接口的出参里提取的字段。提取字段有两种方法,第一种是正则提取,第二种是json数据提取

2、写文件

判断yaml文件的格式,是否有extract参数,接着判断提取的字段是正则提取还是json提取。对请求进行封装,将字段获取之后写入到extract.yaml文件即可。

安装插件:pip.exe install jsonpath -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com SomePackage

通过JSONPath表达式,使得从多层嵌套JSON数据中提取数据变得非常简单。

#规范YAML测试用例
#1、要求包含一级关键字 name,request,validate 
#2、在request一级关键字下,包含method和url字段
#如果是get请求,通过params关键字传参
# 如果是post请求 传json格式,使用json关键字传参
#如果是post请求 传表单格式,使用data关键字传参
#如果是files请求,使用file传参

1、在根目录下,创建extract.yaml文件

2、创建py文件,封装方法,实现对ymal文件格式的验证和方法的请求,并实现extract提取关键字

#!/usr/bin/python
# -*- coding: UTF-8 -*
import json
import re

import jsonpath
import requests

import common.ymal
from debug_talk import DebugTalk


class Unifiedrequest:

    def __init__(self,two,obj):
        #self.url = common.ymal.read_value("base", two)  #这里,从文件获取base,来获取url前面的公共部分
        self.url="" #这里是因为我的url就是全部路径
        #获取一个对象
        self.obj=obj
        #传入的是Debug_Talk文件的类对象,可以调用这个类里面的方法

    #数据替换,data为获取到${}中的数据
    #数据类型可能(string,int,float,list,dict),需要先数据转换
    def read_token(self,data):
        if data:
            #保存原数据类型
            data_type=type(data)
            #数据类型转换
            if isinstance(data,dict) or isinstance(data,list):
                str_data=json.dumps(data)
            else:
                str_data=str(data)
            for ce in range(1,str_data.count("${")+1):
                if "${" in str_data and "}" in str_data:
                    index=str_data.index("${")
                    indexend=str_data.index("}",index)
                    old_value=str_data[index:indexend+1]
                    #获取对象属性
                    fun_name=old_value[2:old_value.index("(")]
                    fun_values=old_value[old_value.index("(")+1:old_value.index(")")]
                    fun_values_new=fun_values.split(",")
                    #*fun_values_new 解包 列表
                    if fun_values_new != " ":
                        #getattr() 函数用于返回一个对象属性值。
                    	#self.obj为对象名称
                        new_value=getattr(self.obj,fun_name)(*fun_values_new)
                        str_data=str_data.replace(old_value,str(new_value))
                    else:
                        new_value = getattr(self.obj,fun_name)()
                        str_data = str_data.replace(old_value, str(new_value))
            if isinstance(data,dict) or isinstance(data,list):
                data=json.loads(str_data)
            else:
                data=data_type(str_data)
            print(data)
        return data

    #yaml文件封装
    def send_yaml(self,arges_name):
        yaml_key = arges_name.keys()
        if "name" in yaml_key and "request" in yaml_key:
            yaml_request=arges_name["request"]
            request_key = yaml_request.keys()
            if "method" in request_key and "url" in request_key:
                method=yaml_request.pop("method")
                url=yaml_request.pop("url")
                res=self.send_request(method,url,**arges_name["request"])
                res_text=res.text
                #状态码
                res_status=res.status_code
                res_json=""
                try:
                    res_json = res.json()
                except Exception as e:
                    print("jsonpath数据类型必须是json格式")
                if "extract" in yaml_key:
                    for key,values in arges_name["extract"].items():
                        if "(.*?)" in values or "(.+?)" in values:
                            zz_value=re.search(values,res_text)
                            if zz_value:
                                common.ymal.write_yaml("extract.yaml",{key:zz_value.group(1)})
                        else:
                            jp_values=jsonpath.jsonpath(res_json,values)
                            if jp_values:
                                common.ymal.write_yaml("extract.yaml",{key:jp_values[0]})

            else:
                print("request中缺少关键字段:method,url")
        else:
            print("yaml文件第一阶段缺少关键字段:name,request")

    session=requests.session()
    def send_request(self,method,url,**kwargs):
        url = self.url + self.read_token(url)
        for key,value in kwargs.items():
            if key in ["params","json","data","headers"]:
                kwargs[key]=self.read_token(value)
            elif key == "files":
                for file_key,file_value in value.items():
                    value[file_key] = open(file_value,"rb")
        res=Unifiedrequest.session.request(method,url,**kwargs)
        print(res.text)
        return res

3、请求yaml文件如下,使用extract关键字,提取某一个值


-  name: 设置cookie接口
   request:
     method: post
     url: http://localhost:8080/set/setcookie
   validate:
     - equals: ["status_code",200 ]
   extract:
     token: '"token":"(.*?)"'
#     或者  $.token

-  name: 获取关联的token值
   request:
      method: post
      url: http://localhost:8080/get/getcookie
      data:
        token: ${read_value(token)}
  
   validate:
        - equals: [ "status_code",200 ]

4、设置conftest.py的夹具方法,清除extrcat.yaml保留的token值

import pytest
from selenium import  webdriver
import common.ymal

@pytest.fixture(scope="session",autouse=True)
def clear():
    common.ymal.clear_yaml("extract.yaml")

5、运行

import pytest
import requests
import yaml
from selenium.webdriver.chrome import webdriver
from selenium.webdriver.common.by import By

import common.ymal
import debug_talk
from common.package import  Unifiedrequest
class TestCase:
        # debug_talk.DebugTalk()是在更目录创建的动态参数类方法
        @pytest.mark.parametrize("args_name", common.ymal.read_yaml("test_package/data.yml"))
        def test_post_tags(self, args_name):
            Unifiedrequest("base_url", debug_talk.DebugTalk()).send_yaml(args_name)





 6、热加载

热加载:就是在代码运行的过程中动态的调用python中的方法达到获得动态参数的目的

在工程目录下,创建debug_talk文件,在里面写一个获取随机数的方法

#!/usr/bin/python
# -*- coding: UTF-8 -*
import random
import time

import common.ymal
from common import ymal


class DebugTalk:

    # 获得随机数
    def get_randon_number(self, min, max):
        return random.randint(int(min), int(max))

    def get_time(self, key):
        return int(time.time())

    # 读取鉴权码yaml文件--token.yaml
    def read_value(self,key):
        return common.ymal.read_value("extract.yaml",key)

封装文件内修改一部分:

                if "${" in str_data and "}" in str_data:
                    index=str_data.index("${")
                    indexend=str_data.index("}",index)
                    old_value=str_data[index:indexend+1]
                    #获取对象属性
                    fun_name=old_value[2:old_value.index("(")]
                    fun_values=old_value[old_value.index("(")+1:old_value.index(")")]
                    fun_values_new=fun_values.split(",")
                    #*fun_values_new 解包 列表
                    if fun_values_new != " ":
                        #getattr() 函数用于返回一个对象属性值。
                        new_value=getattr(self.obj,fun_name)(*fun_values_new)
                        str_data=str_data.replace(old_value,str(new_value))
                    else:
                        new_value = getattr(self.obj,fun_name)()
                        str_data = str_data.replace(old_value, str(new_value))

然后,可以在ymal文件中动态获取这个参数


-  name: 设置cookie接口
   request:
     method: post
     url: http://localhost:8080/set/setcookie
   validate:
     - equals: ["status_code",200 ]
   extract:
     token: '"token":"(.*?)"'
#     或者  $.token

-  name: 获取关联的token值
   request:
      method: post
      url: http://localhost:8080/get/getcookie
      data:
        token: ${read_value(token)}

   validate:
        - equals: [ "status_code",200 ]

-  name: 获取随机数字
   request:
      method: post
      url: http://localhost:8080/send/login
      data:
        username=${get_randon_number(100,999)}&password=${get_randon_number(1000,9999)}
   validate:
        - equals: [ "status_code",200 ]

 参考

pytest框架之热加载_python 热加载_Beck_k的博客-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ES6-ES11基本全部语法

在进入es6语法之前,先走一波es5遍历迭代Api,,它们的作用,应用场景,参数,以及返回值分别是什么。(forEach、map、some、every、filter)我们统一设定一个初始数组:let arra…

Prophet 处理时间序列数据

Prophet 处理时间序列数据 flyfish 论文地址 https://peerj.com/preprints/3190/ 官网 https://facebook.github.io/prophet/ 源码地址 https://github.com/facebook/prophet hon import pandas as pd from prophet import Prophet df pd.read_csv(https://raw.githubuse…

2月23号作业

题目:题目一:通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作--->上传CSDN 1.例如在串口输入led1on,开饭led1灯点亮 2.例如在串口输入led1off,开饭led1灯熄灭 3.例如在串口输入led2on,开饭led2灯点亮 4.例如在串口输…

[HarekazeCTF2019]Easy Notes

知识点:session 反序列化,代码审计代码分析 flag.php 中有个 is_admin 函数的判断。 在 lib.php 中有 is_admin 函数,需要 session[admin] 为 true,或者通过文件读取的方式。 在 index.php 中的 include 并不能使用伪协议读取 …

JVM回顾与Java虚拟机的内存管理

目录 什么是JVM? 主流虚拟机 JVM与操作系统关系 JVM、JRE、JDK的关系 Java程序的执行过程 JVM翻译字节码有三种执行方式 Java虚拟机的内存管理 JVM整体架构图 JVM运行时内存 Java7和Java8内存结构的不同主要体现在方法区的实现 对于Java8,HotSp…

Hadoop MapReduce基本概念与详细流程

Hadoop MapReduce是Hadoop 中一个批量计算的框架,在整个mapreduce作业的过程中,包括从数据的输入,数据的处理,数据的数据输入这些部分,而其中数据的处理部分就要map,reduce,combiner等操作组成。…

刚接手的APP项目需要优化,需要从哪些方向入手?

对于每个Android 开发团队来说产品上线,是让人喜忧参半的一件事。**喜指的是:付出了大量的时间,产品终于上线了;而忧指的是:担心中间会不会出现一些性能相关的问题,比如卡顿、内存泄漏、崩溃……等&#xf…

干翻 nio ,王炸 io_uring 来了 !!(图解+史上最全)

大趋势:全链路异步化,性能提升10倍 随着业务的发展,微服务应用的流量越来越大,使用到的资源也越来越多。 在微服务架构下,大量的应用都是 SpringCloud 分布式架构,这种架构总体上是全链路同步模式。 全链…

java 抽象类 详解

目录 一、抽象类概述: 二、抽象方法 : 1.概述 : 2.应用 : 3.特点 : 三、抽象类特点 : 1.关于abstract关键字 : 2.抽象类不能被实例化,只能创建其子类对象 : 3.抽象类子类的两个选择 : 四、抽象类的成员 : 1.成员变量 : 2.成员方…

趣味三角——第12章——tanx

第12章节 tanx In his very numerous memoires, and especially in his great work, Introductio in analysin infinitorum (1748), Euler displayed the most wonderful skill in obtaining a rich harvest of results of great interest. . . . Hardly any other work …

业务单据堆积如山?如何提升会计做账效率?

某集团以“创建现代能源体系、提高人民生活品质”为使命,形成了贯通下游分销、中游贸易储运、上游生产的清洁能源产业链和涵盖健康、文化、旅游、置业的生命健康产品链。目前,某集团在全国21个省,为超过2681万个家庭用户、21万家企业提供能源…

Android:同步屏障的简单理解和使用

同步屏障的简单理解和使用1、背景2、何为同步屏障?2.1、 发送屏障消息——postSyncBarrier2.2、发送异步消息2.3、处理消息2.4、移除屏障消息——removeSyncBarrier2、系统什么时候添加同步屏障?参考1、背景 这里我们假设一个场景:我们向主线…

网狐服务端C++引入http功能剖析

旗舰版本的网狐服务端及以前的版本都是没有http解析功能的,导致就是web后台改了配置不能及时通知到游戏里面去,以至于很多小公司拿这种框架来开发的变通的方案就是用定时器不定时去刷数据库,导致多少个功能就有多少个定时去刷新,代…

0基础学习软件测试难么

0基础开始学习任何一样事情都是有一定难度的,但是也要有对比,软件测试相比于IT行业其他学科已经算是容易入门的了,就看你个人的学习方法,找的学习资源以及你的自制力。 正确学习方法路径 “我一听就懂,一敲就废&…

【Spring的事务传播行为有哪些呢?Spring事务的隔离级别?讲下嵌套事务?】

如果你想寻求一份与后端相关的开发工作,那么关于Spring事务相关的面试题你就不能说不会并且不能不知道? 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步? 一.Spring中声明事务的方式 1.1 编程式事务 编程…

Android Gradle脚本打包

1、背景资料 1.1 Android-Gradle-Groovy-Java-JVM 之间的关系 1.2 Android Gradle Plugin Version版本 与 Gradle Version版本的对应关系 Android Gradle Plugin Version版本Gradle Version版本1.0.0 - 1.1.32.2.1 - 2.31.2.0 - 1.3.12.2.1 - 2.91.5.02.2.1 - 2.132.0.0 -…

WFP网络过滤驱动-限制网站访问

文章目录前言WFP入门介绍WFP基本架构名词解释代码基本结构代码示例前言 WFP Architecture - Win32 apps | Microsoft Learn是一个网络流量处理平台。WFP 由一组连接到网络堆栈的钩子和一个协调网络堆栈交互的过滤引擎组成。 本文使用WFP,实现了一个网络阻断的demo…

Guna UI WinForms 2.0.4.4 Crack

Guna.UI2 WinForms is the suite for creating groundbreaking desktop app UI. It is for developers targeting the .NET Windows Forms platform. 50 多个 UI 控件 具有广泛功能的综合组件可帮助您开发任何东西。 无尽的定制 只需拖放即可创建视觉效果命令和体验。 出色的…

服务端开发之Java备战秋招面试篇2-HashMap底层原理篇

现在Java应届生和实习生就业基本上必问HashMap的底层原理和扩容机制等,可以说是十分常见的面试题了,今天我们来好好整理一下这些知识,为后面的秋招做足准备,加油吧,少年。 目录 1、HashMap集合介绍 2、HashMap的存储…

S2-001漏洞分析

首发于个人博客:https://bthoughts.top/posts/S2-001漏洞分析/ 一、简介 1.1 Struts2 Struts2是流行和成熟的基于MVC设计模式的Web应用程序框架。 Struts2不只是Struts1下一个版本,它是一个完全重写的Struts架构。 1.2 S2-001 Remote code exploit o…