接口自动化 —— 接口关联的封装、统一请求封装、数据驱动

1123 篇文章 44 订阅
843 篇文章 2 订阅

一、类之间继承问题:

由于在test_api.py定义了全局变量,而test.user.py想使用这些全局变量就得导入test_api.py模块

但是因为test_user.py导入了test_api.py的模块,到时候执行test_user.py的时候会把test.api.py中的方法(用例)也执行一遍

#演示:

目录结构:

test_api.py

import requests

class TestRequest():

    #全局变量,类变量,通过类名调用
    access_token=""
    sess=requests.session()  #定义全局变量sess=requests.session(),将所有的requests都替换为requests.session()就相当于都在一个会话中了

    # 获取接口统一鉴权码token
    def test_token(self):
        url = "http://47.108.153.47:8089/qzcsbj/user/login"
        shuju={
            "username": "ddance",
            "password": "000000"
        }
        res=TestRequest.sess.request(method="post",url=url,json=shuju)  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())
        TestRequest.access_token=res.json()["data"]["token"]  #将token的值赋给全局变量access_token

    #修改一个商品
    def test_edit_product(self):
        url = "http://47.108.153.47:8089/qzcsbj/product/update"
        shuju = {
            "product": {
                "id": 381,
                "price": 20,  #将价格改为20元
                "productName": "狗粮"
            },
            "token": TestRequest.access_token
        }
        res=TestRequest.sess.request(method="post",url=url,json=shuju)  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())

    #文件上传
    def test_file_upload(self):
        shuju = {
            "name": "01"
        }
        shuju1={
            "uploadfile":open(r"C:\01.jpg","rb"),  #r:转义 rb:转化为只读的二进制流
        }
        url = "http://47.108.153.47:8089/qzcsbj/file/upload?name=" + shuju["name"]  #接口文档中规定要将name的值跟在url之后,因此这里使用字典key提取name的值01拼接到url之后
        res=TestRequest.sess.request(method="post",url=url,json=shuju,files=shuju1)  #上传文件的时候使用files而不是data或者json  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())

test_user.py

import requests
from testcases.test_api import TestRequest  #导入testcases包下的test_api模块下的TestRequest类

class TestUser():
    
    #修改用户信息
    def test_edit_user(self):
        shuju={
            "token": TestRequest.access_token,  #使用TestRequest类的全局变量access_token
            "user": {
                "id": 1305,
                "username": "ddance",
                "password": "000000",
                "realName": "string",
                "sex": "0",
                "birthday": "2001-01-01",
                "phone": "13542588888",  #修改电话号码为13542588888
                "utype": "0",
                "addtime": "2023-05-12 22:21:56.0",
                "adduser": "ddance"
                }
        }
        url = "http://47.108.153.47:8089/qzcsbj/user/update"
        res=requests.request(method="post",url=url,json=shuju)
        print(res.json())

all.py

import pytest

if __name__ == '__main__':
    pytest.main()

执行结果:

D:\JetBrains\PycharmProjects\API接口测试\venv\Scripts\python.exe D:\JetBrains\PycharmProjects\API接口测试\all.py 
============================= test session starts =============================

testcases\test_api.py ...                                                [ 42%]  #这里3个“.”表示执行了3个用例
testcases\test_user.py ....                                              [100%]  #这里4个“.”表示执行了4个用例

============================== 7 passed in 0.93s ==============================

Process finished with exit code 0

为什么test_user.py中明明一个用例却执行了4个用例呢?这是因为test_user.py调用test_api.py下的类的时候把test_api.py类下的3个用例也执行了一遍

如何避免这种错误呢,可以去掉全局变量,用YAML文件代为保存全局变量:

#演示:

目录结构:#新增一个ymal空文件extract.yaml,使用yaml_util.py模块将test_api.py产生的全局变量access_token存储到extract.yaml,test_user.py可以在extract.yaml中提取使用access_token

yaml_util.py

import yaml  #需要安装模块:pip install pyyaml
import os

#读取
def read_yaml(key):
    with open(os.getcwd()+'/extract.yaml',mode='r',encoding='utf-8') as f:  #os.getcwd()方法用于返回当前工作目录 #mode='a':追加|'w':覆盖|'r':只读 #encoding编码格式
        print('这里是打印'+os.getcwd()+'/extract.yaml')
        value=yaml.load(stream=f,Loader=yaml.FullLoader)  #yaml.load()读取yaml文件 #stream=f要读取的对象 #Loader=yaml.FullLoader全部提取
        return value[key]  #value是字典类型的,可以通过关键字方式输出值

#写入
def write_yaml(data):
    with open(os.getcwd()+'/extract.yaml',mode='a',encoding='utf-8') as f:
        yaml.dump(data,stream=f,allow_unicode=True)  #yaml.dump()写入数据到yaml文件 #allow_unicode=True允许unicode方式

#清空
def clear_yaml():
    with open(os.getcwd()+'/extract.yaml',mode='w',encoding='utf-8') as f:
        f.truncate()  #清空文件内容

test_api.py  #1、导入yaml_util.py模块 2、删除全局变量access_token  3、修改test_token用例中保存token的方式(存到文件中) 4、修改test_edit_product用例中获取token的方式(从文件中读取)

import requests
from common.yaml_util import read_yaml,write_yaml  #修改点1

class TestRequest():

    #全局变量,类变量,通过类名调用
    ##删除access_token=""  #修改点2
    sess=requests.session()  #定义全局变量sess=requests.session(),将所有的requests都替换为requests.session()就相当于都在一个会话中了

    # 获取接口统一鉴权码token
    def test_token(self):
        url = "http://47.108.153.47:8089/qzcsbj/user/login"
        shuju={
            "username": "ddance",
            "password": "000000"
        }
        res=TestRequest.sess.request(method="post",url=url,json=shuju)  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())
        write_yaml({"access_token":res.json()["data"]["token"]})  #将{"access_token":token的值}键值对写入extract.yaml文件  #修改点3

    #修改一个商品
    def test_edit_product(self):
        url = "http://47.108.153.47:8089/qzcsbj/product/update"
        shuju = {
            "product": {
                "id": 381,
                "price": 20,  #将价格改为20元
                "productName": "狗粮"
            },
            "token": read_yaml("access_token")  #读取extract.yaml文件中key为access_token对应的值  #修改点4
        }
        res=TestRequest.sess.request(method="post",url=url,json=shuju)  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())

    #文件上传
    def test_file_upload(self):
        shuju = {
            "name": "01"
        }
        shuju1={
            "uploadfile":open(r"C:\01.jpg","rb"),  #r:转义 rb:转化为只读的二进制流
        }
        url = "http://47.108.153.47:8089/qzcsbj/file/upload?name=" + shuju["name"]  #接口文档中规定要将name的值跟在url之后,因此这里使用字典key提取name的值01拼接到url之后
        res=TestRequest.sess.request(method="post",url=url,json=shuju,files=shuju1)  #上传文件的时候使用files而不是data或者json  #将所有的requests都替换为requests.session()就相当于都在一个会话中了
        print(res.json())

test_user.py  #1、删除导入test_api.py中全局变量的方法  2、导入yaml_util.py模块 3、修改test_edit_user用例中获取token的方式(从文件中读取)

import requests
##删除from testcases.test_api import TestRequest  #导入testcases包下的test_api模块下的TestRequest类 #修改点1
from common.yaml_util import read_yaml,write_yaml #修改点2

class TestUser():

    #修改用户信息
    def test_edit_user(self):
        shuju={
            "token": read_yaml("access_token"),  #读取extract.yaml文件中key为access_token对应的值 #修改点3
            "user": {
                "id": 1305,
                "username": "ddance",
                "password": "000000",
                "realName": "string",
                "sex": "0",
                "birthday": "2001-01-01",
                "phone": "13542588888",  #修改电话号码为13542588888
                "utype": "0",
                "addtime": "2023-05-12 22:21:56.0",
                "adduser": "ddance"
                }
        }
        url = "http://47.108.153.47:8089/qzcsbj/user/update"
        res=requests.request(method="post",url=url,json=shuju)
        print(res.json())

all.py

import pytest

if __name__ == '__main__':
    pytest.main(['-vs'])

执行结果:

D:\JetBrains\PycharmProjects\API接口测试\venv\Scripts\python.exe D:\JetBrains\PycharmProjects\API接口测试\all.py 
============================= test session starts =============================

testcases/test_api.py::TestRequest::test_token {'code': 2003, 'msg': '登录成功', 'data': {'token': 'ddance_c08566ea-529b-4817-a8cf-edf20a3da6e8'}}
PASSED
testcases/test_api.py::TestRequest::test_edit_product {'code': 3005, 'msg': '更新商品成功', 'data': {'id': 381, 'productName': '狗粮', 'price': 20.0}}
PASSED
testcases/test_api.py::TestRequest::test_file_upload {'code': 7001, 'msg': '文件上传成功', 'data': '01_01.jpg_4d37c6cc-c3e1-4f46-9fa4-bfd9153a6e34.jpg'}
PASSED
testcases/test_user.py::TestUser::test_edit_user {'code': 2009, 'msg': '更新用户成功', 'data': {'id': 1305, 'username': 'ddance', 'password': '******', 'realName': 'string', 'sex': '0', 'birthday': '2001-01-01', 'phone': '13542588888', 'utype': '0', 'addtime': '2023-05-12 22:21:56.0', 'adduser': 'ddance'}}
PASSED

============================== 4 passed in 0.23s ==============================

Process finished with exit code 0

extract.yaml  #生成了一条token的值

access_token: ddance_c08566ea-529b-4817-a8cf-edf20a3da6e8

以上代码仍有问题,由于write.yaml()选择追加的方式写入数据,多次执行后extract.yaml文件中会有多个access_token:[value]键值对,到时候read.yaml()取值的时候可能会报错,解决办法是增加前后置方法在每次写入文件前清空extract.yaml文件

#演示:

目录结构:#增加前后置固件

conftest.py

import pytest
from common.yaml_util import write_yaml,read_yaml,clear_yaml

#在所有接口请求之前执行
@pytest.fixture(scope='package',autouse=True)  #作用域是包,即testcases目录,并且自动执行,其余文件无需任何修改
def clear_extract():
    clear_yaml()  #这是一个前置

如果test_user.py中用例需要test_api.py中用例的cookie怎么办呢?可以调用test_api.py中全局变量sess=requests.session()使两个文件中的用例处于同一个会话(session),但是这样同样会因为类之间继承的问题导致test_api.py被调用时再执行一次,怎么解决呢,可以使用统一请求封装

将统一会话请求封装到一个包中,然后所有测试用例去调用这个包中的统一请求方法

#演示:

目录结构:#新增文件

request_util.py

import requests

class RequestUtil():

    #全局变量,类变量,通过类名调用
    sess=requests.session()

    def send_request(self,method,url,**kwargs):
        method=str(method).lower()  #防止用户在传入get|post写为大写,可以用lower()转化为小写
        res=RequestUtil.sess.request(method=method,url=url,**kwargs)
        return res

test_api.py

import requests
from common.yaml_util import read_yaml,write_yaml
from common.request_util import RequestUtil  #修改点1

class TestRequest():

    ##删除#全局变量,类变量,通过类名调用
    ##删除sess=requests.session()  #定义全局变量sess=requests.session(),将所有的requests都替换为requests.session()就相当于都在一个会话中了

    # 获取接口统一鉴权码token
    def test_token(self):
        url = "http://47.108.153.47:8089/qzcsbj/user/login"
        shuju={
            "username": "ddance",
            "password": "000000"
        }
        res = RequestUtil().send_request(method="post", url=url, json=shuju)  #修改点2 #RequestUtil在request_util.py中是个类,因此调用其中方法时需要先实例化RequestUtil(RequestUtil后面得带上括号)
        print(res.json())
        write_yaml({"access_token":res.json()["data"]["token"]})  #将{"access_token":token的值}键值对写入extract.yaml文件

    #修改一个商品
    def test_edit_product(self):
        url = "http://47.108.153.47:8089/qzcsbj/product/update"
        shuju = {
            "product": {
                "id": 381,
                "price": 20,  #将价格改为20元
                "productName": "狗粮"
            },
            "token": read_yaml("access_token")  #读取extract.yaml文件中key为access_token对应的值
        }
        res = RequestUtil().send_request(method="post", url=url, json=shuju,headers=None)  #修改点3
        print(res.json())

    #文件上传
    def test_file_upload(self):
        shuju = {
            "name": "01"
        }
        shuju1={
            "uploadfile":open(r"C:\01.jpg","rb"),  #r:转义 rb:转化为只读的二进制流
        }
        url = "http://47.108.153.47:8089/qzcsbj/file/upload?name=" + shuju["name"]  #接口文档中规定要将name的值跟在url之后,因此这里使用字典key提取name的值01拼接到url之后
        res = RequestUtil().send_request(method="post", url=url, json=shuju, files=shuju1)  #修改点4
        print(res.json())

test_user.py

import requests
from common.yaml_util import read_yaml,write_yaml
from common.request_util import RequestUtil  #修改点1

class TestUser():

    #修改用户信息
    def test_edit_user(self):
        shuju={
            "token": read_yaml("access_token"),  #读取extract.yaml文件中key为access_token对应的值
            "user": {
                "id": 1305,
                "username": "ddance",
                "password": "000000",
                "realName": "string",
                "sex": "0",
                "birthday": "2001-01-01",
                "phone": "13542588888",  #修改电话号码为13542588888
                "utype": "0",
                "addtime": "2023-05-12 22:21:56.0",
                "adduser": "ddance"
                }
        }
        url = "http://47.108.153.47:8089/qzcsbj/user/update"
        res = RequestUtil().send_request(method="post", url=url, json=shuju)  #修改点2
        print(res.json())

all.py

import pytest

if __name__ == '__main__':
    pytest.main(['-vs'])

#其余文件不变

执行结果:

D:\JetBrains\PycharmProjects\API接口测试\venv\Scripts\python.exe D:\JetBrains\PycharmProjects\API接口测试\all.py 
============================= test session starts =============================

testcases/test_api.py::TestRequest::test_token {'code': 2003, 'msg': '登录成功', 'data': {'token': 'ddance_deec7593-72fa-4efb-8a8d-30cd46a76c90'}}
PASSED
testcases/test_api.py::TestRequest::test_edit_product {'code': 3005, 'msg': '更新商品成功', 'data': {'id': 381, 'productName': '狗粮', 'price': 20.0}}
PASSED
testcases/test_api.py::TestRequest::test_file_upload {'code': 7001, 'msg': '文件上传成功', 'data': '01_01.jpg_9a6dab59-5d5e-428b-9c81-7afa956d0c32.jpg'}
PASSED
testcases/test_user.py::TestUser::test_edit_user {'code': 2009, 'msg': '更新用户成功', 'data': {'id': 1305, 'username': 'ddance', 'password': '******', 'realName': 'string', 'sex': '0', 'birthday': '2001-01-01', 'phone': '13542588888', 'utype': '0', 'addtime': '2023-05-12 22:21:56.0', 'adduser': 'ddance'}}
PASSED

============================== 4 passed in 0.22s ==============================

Process finished with exit code 0

实际测试环境使用pytest的时候还需要建立数据驱动,所谓数据驱动就是把test_api.py、test_user.py中test_token、test_edit_user等等方法中写的method、url、shuju等变量的内容封装到一个yaml文件中去调用,而不是直接写死到方法中,这个yaml文件就是数据驱动

数据驱动需要用到装饰器@pytest.mark.parametrize(args_name,args_value)  #args_name 参数名,字符串,可自定义名称  #args_value 参数值(list,tuple,字典列表,字典元组),有多少个值那么测试用例就会执行多少次(以"-"为分隔符计算有多少个值)

#YAML介绍:

yaml是一种数据格式,主要用于配置文件或者编写用例

yaml只有两种数据:

  1.键值对

    key:(空格)value

  2.list

    用一个“-”表示一个列表

操作yaml的第三方模块是pyyaml

#演示:

目录结构:#新增文件

 test_api-test_token.yaml

-  #这个"-"是每组数据的分隔符
  name: 获取接口统一鉴权码token  #本组数据名称
  request:  #请求内容
    method: post  #请求方法
    url: http://47.108.153.47:8089/qzcsbj/user/login  #请求url
    shuju:  #请求体
      username: ddance
      password: '000000'  #000000加上引号表示是字符串,防止被识别成int,int只有一位0
  validate: None  #断言,这里选择不使用
#name、request、validate是数据驱动最基本的内容,当然这三个基本内容的名称可自定义

yaml_util.py  #增加一个读取数据驱动的方法read_testcases

import yaml  #需要安装模块:pip install pyyaml
import os

#读取
def read_yaml(key):
    with open(os.getcwd()+'/extract.yaml',mode='r',encoding='utf-8') as f:  #os.getcwd()方法用于返回当前工作目录 #mode='a':追加|'w':覆盖|'r':只读 #encoding编码格式
        value=yaml.load(stream=f,Loader=yaml.FullLoader)  #yaml.load()读取yaml文件 #stream=f要读取的对象 #Loader=yaml.FullLoader全部提取
        return value[key]  #value是字典类型的,可以通过关键字方式输出值

#写入
def write_yaml(data):
    with open(os.getcwd()+'/extract.yaml',mode='a',encoding='utf-8') as f:
        yaml.dump(data,stream=f,allow_unicode=True)  #yaml.dump()写入数据到yaml文件 #allow_unicode=True允许unicode方式

#清空
def clear_yaml():
    with open(os.getcwd()+'/extract.yaml',mode='w',encoding='utf-8') as f:
        f.truncate()  #清空文件内容

#读取数据驱动
def read_testcases(yaml_name):
    with open(os.getcwd()+'/testcases'+'/'+yaml_name,mode='r',encoding='utf-8') as f:
        value=yaml.load(stream=f,Loader=yaml.FullLoader)
        return value

test_user.py #为演示方便把这个文件内容注释掉

test_api.py #为演示方便只保留一个测试用例(test_token()方法)演示

import requests
from common.yaml_util import read_yaml,write_yaml,read_testcases #修改点1
from common.request_util import RequestUtil
import pytest #修改点2

class TestRequest():

    # 获取接口统一鉴权码token
    @pytest.mark.parametrize("args_name",read_testcases("test_api-test_token.yaml")) #修改点3 #args_value的值是read_testcases读取yaml中的内容 #args_name返回的是字典类型
    def test_token(self,args_name):
        url = args_name["request"]["url"]  #从args_name中获取url
        shuju=args_name["request"]["shuju"]  #从args_name中获取shuju
        res = RequestUtil().send_request(method=args_name["request"]["method"], url=url, json=shuju)  #从args_name中获取method
        print(res.json())
        write_yaml({"access_token":res.json()["data"]["token"]})  #将{"access_token":token的值}键值对写入extract.yaml文件
'''
    #修改一个商品
    def test_edit_product(self):
        url = "http://47.108.153.47:8089/qzcsbj/product/update"
        shuju = {
            "product": {
                "id": 381,
                "price": 20,  #将价格改为20元
                "productName": "狗粮"
            },
            "token": read_yaml("access_token")  #读取extract.yaml文件中key为access_token对应的值
        }
        res = RequestUtil().send_request(method="post", url=url, json=shuju,headers=None)
        print(res.json())

    #文件上传
    def test_file_upload(self):
        shuju = {
            "name": "01"
        }
        shuju1={
            "uploadfile":open(r"C:\01.jpg","rb"),  #r:转义 rb:转化为只读的二进制流
        }
        url = "http://47.108.153.47:8089/qzcsbj/file/upload?name=" + shuju["name"]  #接口文档中规定要将name的值跟在url之后,因此这里使用字典key提取name的值01拼接到url之后
        res = RequestUtil().send_request(method="post", url=url, json=shuju, files=shuju1)
        print(res.json())
'''

all.py

import pytest

if __name__ == '__main__':
    pytest.main(['-vs'])

#其余文件不变

执行结果:

D:\JetBrains\PycharmProjects\API接口测试\venv\Scripts\python.exe D:\JetBrains\PycharmProjects\API接口测试\all.py 
============================= test session starts =============================

testcases/test_api.py::TestRequest::test_token[args_name0] {'code': 2003, 'msg': '登录成功', 'data': {'token': 'ddance_3b660bd1-7c26-4791-b183-b4e07326884c'}}
PASSED

============================== 1 passed in 0.13s ==============================

Process finished with exit code 0

一个接口一般对应一个数据驱动yaml文件, 一个yaml可以以“-”为分隔符定义多组测试数据,@pytest.mark.parametrize可以根据有几组测试数据就把这个测试用例跑几遍

#演示

test_api-test_token.yaml

-  #第1组数据
  name: 01获取接口统一鉴权码token
  request:
    method: post
    url: http://47.108.153.47:8089/qzcsbj/user/login
    shuju:
      username: ddance
      password: '000000'  #000000加上引号表示是字符串,防止被识别成int,int只有一位0
  validate: None

-  #第2组数据
  name: 02获取接口统一鉴权码token
  request:
    method: post
    url: http://47.108.153.47:8089/qzcsbj/user/login
    shuju:
      username: ddance
      password: '000000'  #000000加上引号表示是字符串,防止被识别成int,int只有一位0
  validate: None

#其余文件不变

执行结果: #可以看到test_token测试用例被执行了2次,在实际使用时还可以将yaml中第2组数据写一个错误的值以验证测试用例准确性

D:\JetBrains\PycharmProjects\API接口测试\venv\Scripts\python.exe D:\JetBrains\PycharmProjects\API接口测试\all.py 
============================= test session starts =============================

testcases/test_api.py::TestRequest::test_token[args_name0] {'code': 2003, 'msg': '登录成功', 'data': {'token': 'ddance_2d009055-3b49-4ca6-8183-f4ebcf7c27b0'}}
PASSED
testcases/test_api.py::TestRequest::test_token[args_name1] {'code': 2003, 'msg': '登录成功', 'data': {'token': 'ddance_ada0cffb-2a06-4eb5-9920-961446c3e65e'}}
PASSED

============================== 2 passed in 0.16s ==============================

Process finished with exit code 0

如果在加上pytest.ini配置文件一个简单的pytest测试框架就搭建完成了。

最后: 为了回馈铁杆粉丝们,我给大家整理了完整的软件测试视频学习教程,朋友们如果需要可以自行免费领取 【保证100%免费】

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值