当前位置: 首页 > 工具软件 > conftest > 使用案例 >

pytest数据驱动及conftest文件及装饰器使用

况浩邈
2023-12-01

一:数据驱动

file_operate.py文件

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :file_operate.py
    @Author  :gaojs
    @Date    :2022/7/1 23:00
    @Blogs   : https://www.gaojs.com.cn
"""
import openpyxl as xl
import yaml


def read_excel(filepath, sheet_name):
    """
    读取数据,将其转换成所需格式
    :return:
    """
    # 得到整个excel文档对象
    wb = xl.load_workbook(filepath)
    # 获取某个sheet工作表数据
    sheet_data = wb[sheet_name]
    # 定义空列表,用来存储多行数据,每行数据都是一个列表
    data = []
    # 得到总行数
    lines_count = sheet_data.max_row
    # 得到总列数
    cols_count = sheet_data.max_column

    for li in range(2, lines_count+1):
        line = []
        for co in range(1, cols_count+1):
            cell = sheet_data.cell(li, co).value
            if cell is None:
                cell = ''
            line.append(cell)
        data.append(line)
    return data


def write_excel(filepath, sheet_name, row, col, text):
    """
    写入内容
    :return:
    """
    wb = xl.load_workbook(filepath)
    sheet_data = wb[sheet_name]
    # 写入数据
    sheet_data.cell(row=row, column=col, value=text)
    wb.save(filepath)


def read_yaml(filepath):
    """
    读取yaml文件
    :return:
    """
    with open(filepath, mode='r', encoding='utf-8') as fin:
        content = yaml.load(fin, Loader=yaml.FullLoader)
        return content


def write_yaml(filepath, text):
    """
    写入文件内容
    :return:
    """
    with open(filepath, mode='w', encoding='utf-8') as fin:
        yaml.dump(text, fin, Dumper=yaml.Dumper)


if __name__ == '__main__':
    # print(read_excel('./test_data.xlsx', '数据驱动数据'))
    # write_excel('./test_data.xlsx', '数据驱动数据', row=6, col=6, text='gaojianshuai')
    print(read_yaml('./test_data.yml').get('数据驱动数据'))
    write_yaml('./test_data1.yml', {'userName': 'gaojs', 'password': '1234'})

test_data.yml文件

# 数据驱动接口数据

数据驱动数据:
  - ['admin', 1234, 200, 0, 'success']
  - ['', 1234, 200, 1, '参数为空']
  - ['admin', '', 200, 1, '参数为空']

二、pytest记录

conftest.py文件

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :conftest.py
    @Author  :gaojs
    @Date    :2022/6/30 21:47
    @Blogs   : https://www.gaojs.com.cn
"""
from typing import List

import pytest
import requests

from 码同学.requests_study.cookie_study import login


@pytest.fixture(scope='session', autouse=True)
def login_and_logout():
    print('在conftest.py中定义fixture,自动执行')
    login(userName='admin', password='1234')
    print('在档次执行测试中只执行一次,因为作用域是session')
    yield
    print('在当前执行测试只执行一次后置动作,因为作用域是session')


# 重写pytest的一个hook函数,处理pycharm插件界面显示的执行结果乱码
def pytest_collection_modifyitems(items:List["item"]):
    for item in items:
        item.name = item.name.encode("utf-8").decode("unicode-escape")
        item._nodeid = item._nodeid.encode("utf-8").decode("unicode-escape")


@pytest.fixture(scope='module', autouse=True)
def VPN0():
    print('在conftest.py中定义fixture,自动执行')
    login(userName='admin', password='1234')
    print('在当前每个用例执行一次,因为作用域是module')
    yield
    print('在当前每个用例执行后执行一次,因为作用域是module')

pytest.ini

不能有中文

[pytest]
addopts = -sv -n auto --dist=loadfile

testpaths = ./

python_files = test*.py

python_classes = Test*

python_functions = test_*

test_param_caresian.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_param_login.py
    @Author  :gaojs
    @Date    :2022/6/30 22:44
    @Blogs   : https://www.gaojs.com.cn
"""
import pytest
import requests

# 第一步:将测试数据转换成python中列表套列表的格式
from 码同学.requests_study.cookie_study import login
from 码同学.requests_study.put_api_study import post_update_phone_info


# 第二:使用pytest装饰器,将其传递给测试用例函数
brand = ['Apple', 'xiaomi', 'sanxing']
color = ['red', 'yellow', 'black']
memorySize = ['256G', '128G', '64G', '512G']
cpuCore=['8核', '4核', '16核']
expect_status_code = [200]
expect_code = ['0']
expect_message = ['更新成功']


@pytest.mark.parametrize('brand', brand)
@pytest.mark.parametrize('color', color)
@pytest.mark.parametrize('memorySize', memorySize)
@pytest.mark.parametrize('cpuCore', cpuCore)
@pytest.mark.parametrize('expect_status_code', expect_status_code)
@pytest.mark.parametrize('expect_code', expect_code)
@pytest.mark.parametrize('expect_message', expect_message)
def test_put(brand, color, memorySize, cpuCore, expect_status_code, expect_code, expect_message):
    """
    笛卡尔积参数化
    :return:
    """
    resp = post_update_phone_info(brand=brand, color=color, memorySize=memorySize, cpuCore=cpuCore)
    status_code = resp.status_code
    assert status_code == expect_status_code
    # 断言code
    resp_json = resp.json()
    code = resp_json['code']
    assert code == expect_code
    # message断言
    result = resp_json['message']
    assert result == expect_message


# 异常组合不适合笛卡尔积,只能对有效值多个值进行使用

test_param_login.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_param_login.py
    @Author  :gaojs
    @Date    :2022/6/30 22:44
    @Blogs   : https://www.gaojs.com.cn
"""
import pytest
import requests
from ..datadriver.file_operate import read_yaml, read_excel

# 第一步:将测试数据转换成python中列表套列表的格式
from 码同学.requests_study.cookie_study import login

# 第一种数据给定:指定在文件中
# test_data = [
#     ['admin', '1234', 200, '0', 'success'],
#     ['', '1234', 200, '1', '参数为空'],
#     ['admin', '', 200, '1', '参数为空']
# ]
# 第二种,通过调用file_operate文件中的方法读取excel数据
# test_data = read_excel(r'E:\爬虫\pachong-master\码同学\datadriver\test_data.xlsx', '数据驱动数据')
test_data = read_yaml(r'E:\爬虫\pachong-master\码同学\datadriver\test_data.yml').get('数据驱动数据')
print(test_data)


@pytest.mark.parametrize('userName, password, expect_status_code, expect_code, expect_message', test_data)
def test_login_param(userName, password, expect_status_code, expect_code, expect_message):
    """
    数据驱动:登录测试
    :return:
    """
    resp = login(userName=userName, password=password)
    status_code = resp.status_code
    assert status_code == expect_status_code
    # 断言code
    resp_json = resp.json()
    code = resp_json['code']
    assert code == expect_code
    # message断言
    result = resp_json['message']
    assert result == expect_message

test_by_func.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_by_func.py
    @Author  :gaojs
    @Date    :2022/6/30 21:55
    @Blogs   : https://www.gaojs.com.cn
"""
# 以函数形式编写测试用例

# 测试用例
from 码同学.requests_study.cookie_study import login


def test_login():
    """
    登录测试用例
    :return:
    """
    resp = login(userName='admin', password='1234')
    status_code = resp.status_code
    assert status_code == 200
    # 业务断言,
    resp_json = resp.json()
    # print(resp_json)
    code = resp_json['code']
    assert code == '0'
    result = resp_json['message']
    assert result == 'success'


def test_login_userisnull():
    """
    登录测试用例
    :return:
    """
    resp = login(userName='', password='1234')
    status_code = resp.status_code
    assert status_code == 200
    # 业务断言,
    resp_json = resp.json()
    print(resp_json)
    code = resp_json['code']
    assert code == '1'
    result = resp_json['message']
    assert result == '参数为空'


# 运行代码方式
"""
1.运行单个用例
pytest .\码同学\pytest_study\test_by_class.py::TestLogin::test_login_userisnull
2.运行整个类下 的所有用例
pytest -sv .\码同学\pytest_study\test_by_class.py
3.右键执行所有用例:光标放在所有第一个方法顶部

4.右键执行某一个用例:贯标放在对应的函数上
"""

test_by_class.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_by_class.py
    @Author  :gaojs
    @Date    :2022/6/30 21:55
    @Blogs   : https://www.gaojs.com.cn
"""
# 以类形式编写测试用例

# 测试用例
from 码同学.requests_study.cookie_study import login


class TestLogin:

    # 测试用例1
    def test_login(self):
        """
        登录测试用例
        :return:
        """
        resp = login(userName='admin', password='1234')
        status_code = resp.status_code
        assert status_code == 200
        # 业务断言,
        resp_json = resp.json()
        # print(resp_json)
        code = resp_json['code']
        assert code == '0'
        result = resp_json['message']
        assert result == 'success'

    # 测试用例2
    def test_login_userisnull(self):
        """
        登录测试用例
        :return:
        """
        resp = login(userName='', password='1234')
        status_code = resp.status_code
        assert status_code == 200
        # 业务断言,
        resp_json = resp.json()
        print(resp_json)
        code = resp_json['code']
        assert code == '1'
        result = resp_json['message']
        assert result == '参数为空'


# 运行代码方式
"""
1.运行单个用例
pytest .\码同学\pytest_study\test_by_class.py::TestLogin::test_login_userisnull
2.运行整个类下 的所有用例
pytest -sv .\码同学\pytest_study\test_by_class.py
3.右键执行所有用例
"""

test_preport_function.py

setup_function

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_prepost_module.py
    @Author  :gaojs
    @Date    :2022/6/30 22:18
    @Blogs   : https://www.gaojs.com.cn
"""
# 查询余额接口:首先登陆,然后查询,负责提示未登录

from 码同学.requests_study.cookie_study import login, query


def setup_function():
    print('\n************setup_function:在当前脚本文件中,每个用例执行之前,都会执行一次登陆**************')
    # 登陆接口
    login(userName='admin', password='1234')


def teardown_function():
    print('***********teardown_function: 在当前脚本文件中,每个测试用例执行后,都会执行一次**************')


# 测试用例1:查询余额正确
def test_query():
    """
    正常查询操作
    :return:
    """
    resp = query(userName='admin')
    status = resp.status_code
    assert status == 200
    resp_json = resp.json()
    code = resp_json['code']
    assert code == '0'
    result = resp_json['message']
    assert result == 'success'


# 测试用例2:错误的用户名
def test_query_error():
    """
    正常查询操作
    :return:
    """
    resp = query(userName='gaojs')
    status = resp.status_code
    assert status == 200
    resp_json = resp.json()
    code = resp_json['code']
    assert code == '1'
    result = resp_json['message']
    assert result == '用户未登录'

test_prepost_method.py

setup_method

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_prepost_module.py
    @Author  :gaojs
    @Date    :2022/6/30 22:18
    @Blogs   : https://www.gaojs.com.cn
"""
# 查询余额接口:首先登陆,然后查询,负责提示未登录
import pytest

from 码同学.requests_study.cookie_study import login, query


class TestQuery:

    def setup_method(self):
        print('\n************setup_method:在当前类中,每条测试用例执行之前,都会执行一次登陆**************')
        # 登陆接口
        login(userName='admin', password='1234')

    def teardown_method(self):
        print('***********teardown_method: 在当前类中,每条测试用例执行之后,都会执行一次**************')

    # 测试用例1:查询余额正确
    def test_query(self):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='admin')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '0'
        pytest.assume(code == '0', f'实际值:{code},期望值是0')
        result = resp_json['message']
        # assert result == 'success'
        pytest.assume(result == 'success', f'实际值:{result},期望值是success')

    # 测试用例2:错误的用户名
    def test_query_error(self):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='gaojs')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '1'
        result = resp_json['message']
        assert result == '用户未登录'

test_propost_module.py

setup_module

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_prepost_module.py
    @Author  :gaojs
    @Date    :2022/6/30 22:18
    @Blogs   : https://www.gaojs.com.cn
"""
# 查询余额接口:首先登陆,然后查询,负责提示未登录

from 码同学.requests_study.cookie_study import login, query


def setup_module():
    print('\n************setup:在当前脚本文件中,所有执测试执行前,只执行一次登陆**************')
    # 登陆接口
    login(userName='admin', password='1234')


def teardown_module():
    print('***********teardown: 在当前脚本文件中,所有执测试执行后,只执行一次**************')


# 测试用例1:查询余额正确
def test_query():
    """
    正常查询操作
    :return:
    """
    resp = query(userName='admin')
    status = resp.status_code
    assert status == 200
    resp_json = resp.json()
    code = resp_json['code']
    assert code == '0'
    result = resp_json['message']
    assert result == 'success'


# 测试用例2:错误的用户名
def test_query_error():
    """
    正常查询操作
    :return:
    """
    resp = query(userName='gaojs')
    status = resp.status_code
    assert status == 200
    resp_json = resp.json()
    code = resp_json['code']
    assert code == '1'
    result = resp_json['message']
    assert result == '用户未登录'

test_prepost_class.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :test_prepost_module.py
    @Author  :gaojs
    @Date    :2022/6/30 22:18
    @Blogs   : https://www.gaojs.com.cn
"""
# 查询余额接口:首先登陆,然后查询,负责提示未登录
import pytest

from 码同学.requests_study.cookie_study import login, query


# @pytest.fixture(scope='class', autouse=True)
# def login_and_logout():
#     print('\n************setup_class:在当前类中,在每个类执行之前,都会执行一次登陆**************')
#     login()
#     yield
#     print('***********teardown_class: 在当前类中,在每个类执行之后,都会执行一次**************')


class TestQuery:

    # def setup_class(self):
    #     print('\n************setup_class:在当前类中,在每个类执行之前,都会执行一次登陆**************')
    #     # 登陆接口
    #     login(userName='admin', password='1234')
    #
    # def teardown_class(self):
    #     print('***********teardown_class: 在当前类中,在每个类执行之后,都会执行一次**************')

    # 测试用例1:查询余额正确
    def test_query(self, login_and_logout):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='admin')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '0'
        result = resp_json['message']
        assert result == 'success'

    # 测试用例2:错误的用户名
    def test_query_error(self, login_and_logout):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='gaojs')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '1'
        result = resp_json['message']
        assert result == '用户未登录'


class TestQuery1:


    # 测试用例1:查询余额正确
    def test_query1(self, login_and_logout):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='admin')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '0'
        result = resp_json['message']
        assert result == 'success'

    # 测试用例2:错误的用户名
    def test_query_error1(self, login_and_logout):
        """
        正常查询操作
        :return:
        """
        resp = query(userName='gaojs')
        status = resp.status_code
        assert status == 200
        resp_json = resp.json()
        code = resp_json['code']
        assert code == '1'
        result = resp_json['message']
        assert result == '用户未登录'

readME文件

总结:
1.模块级别最大,因为不管是函数式测试用例,还是类方式的测试用例,都在一个模块中
2.函数级别仅仅是针对,函数式测试用例生效(setup_function()不能再类方式中使用,类方式中要使用setup_method())
3.类级别和方法级别,是针对于类方式编写的测试用例生效
4.setup和teardown是可以替换函数级别和方法级别的前置后置


1.运行单个用例
pytest .\码同学\pytest_study\test_by_class.py::TestLogin::test_login_userisnull
2.运行整个类下 的所有用例
pytest -sv .\码同学\pytest_study\test_by_class.py
3.右键执行所有用例:光标放在所有第一个方法顶部

4.右键执行某一个用例:贯标放在对应的函数上



异常组合不适合笛卡尔积,只能对有效值多个值进行使用

三、requests记录

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :cookie_study.py
    @Author  :gaojs
    @Date    :2022/6/28 22:50
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


# 多个接口使用同一个session对象的话,他会自动的帮我们去管理和关联cookie
session = requests.session()


def login(userName='admin', password='1234'):
    """
    登录操作
    :return:
    """
    url = 'http://localhost:9090/bank/api/login'
    data = {
        'userName': userName,
        'password': password
    }
    rsp = session.post(url=url, data=data)
    result = rsp.json().get('message')
    return rsp


def query(userName='admin'):
    """
    银行余额查询接口
    :return:
    """
    url = 'http://localhost:9090/bank/api/query'
    params = {
        'userName': userName
    }
    rsp = session.get(url=url, params=params)
    print(rsp.text)
    result = rsp.json().get('message')
    print(result)
    money_data = rsp.json().get('data')
    print(money_data)
    return rsp


login()
query()

delete_api.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :delete_api_study.py
    @Author  :gaojs
    @Date    :2022/6/28 23:26
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


def delete_phone_info():
    """
    put请求
    更新手机信息
    :return:
    """
    url = 'http://localhost:9090/com/phone'
    data = {
        "brand": "Apple", "color": "red",
        "memorySize": "256G", "cpuCore": "8核",
        "price": "10080", "desc": "全新上市"
    }
    rsp = requests.delete(url=url, json=data)
    print(rsp.text)
    result = rsp.json().get('message')
    print(result)

file_upload.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :file_upload_study.py
    @Author  :gaojs
    @Date    :2022/6/28 23:17
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


session = requests.session()


def file_upload():
    """
    上传文件
    :return:
    """
    url = 'http://localhost:9090/file/api/upload2'
    file = {
        'file': open(r'C:\Users\Administrator\Desktop\123.jpg', mode='rb')
    }
    rsp = session.post(url=url, files=file)
    result = rsp.json().get('message')
    print(result)


if __name__ == '__main__':
    file_upload()

post_api.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :post_api_study.py
    @Author  :gaojs
    @Date    :2022/6/28 22:43
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


def post_api():
    """
    post请求
    :return:
    """
    url = 'http://localhost:9090/com/login'
    data = {
        'userName': 'admin',
        'password': '1234'
    }
    rsp = requests.post(url=url, data=data)
    result = rsp.json().get('message')
    print(result)


def post_add_phone_info():
    """
    新增手机信息
    :return:
    """
    url = 'http://localhost:9090/com/phone'
    data = {
        "brand": "Huawei", "color": "yellow",
        "memorySize": "64G", "cpuCore": "8核",
        "price": "8848", "desc": "全新上市"
    }
    rsp = requests.post(url=url, json=data)
    result = rsp.json().get('message')
    print(result)

put_api.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :put_api_study.py
    @Author  :gaojs
    @Date    :2022/6/28 22:49
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


def post_update_phone_info(brand='Apple', color='red', memorySize='256G', cpuCore='8核'):
    """
    put请求
    更新手机信息
    :return:
    """
    url = 'http://localhost:9090/com/phone'
    data = {
        "brand": brand,
        "color": color,
        "memorySize": memorySize,
        "cpuCore": cpuCore,
        "price": "10080",
        "desc": "全新上市"
    }
    rsp = requests.put(url=url, json=data)
    result = rsp.json().get('message')
    print(result)
    return rsp


post_update_phone_info()

token_study.py

# coding=utf-8
"""
    @Project :pachong-master 
    @File    :token_study.py
    @Author  :gaojs
    @Date    :2022/6/28 23:01
    @Blogs   : https://www.gaojs.com.cn
"""
import requests


session = requests.session()


def login(userName='admin', password='1234'):
    """
    获取token
    :return:
    """
    url = 'http://localhost:9090/bank/api/login2'
    data = {
        'userName': userName,
        'password': password
    }
    rsp = session.post(url=url, data=data)
    print(rsp.text)
    return rsp


def post_api_token(userName='admin', password='1234'):
    """
    获取token
    :return:
    """
    url = 'http://localhost:9090/bank/api/login2'
    data = {
        'userName': userName,
        'password': password
    }
    rsp = session.post(url=url, data=data)
    print(rsp.text)
    msg = rsp.json().get('message')
    print(msg)
    global token
    token = rsp.json().get('data')
    print(token)
    return token


def query_money():
    """
    银行余额查询接口
    :return:
    """
    url = 'http://localhost:9090/bank/api/query2'
    params = {
        'userName': 'admin'
    }
    headers = {
        'testfan-token': token
    }
    rsp = session.get(url=url, headers=headers, params=params)
    print(rsp.text)
    result = rsp.json().get('data')
    print(result)


if __name__ == '__main__':
    post_api_token()
    query_money()

readME

总结:
requests库的请求方法参数众多,划分如下:

global全局参数使用:不通的函数中如果想使用的话就用global


查询参数就用params=params
表单参数就用data=data
json参数就用json=json
文件参数就用files=files
请求头信息headers就用headers=headers
 类似资料: