搭建Pytest自动化测试框架(学习笔记)

学习更新ing…

相关知识

基本知识

命名规则

py文件以及函数名需要以test_开头
在这里插入图片描述

运行方式

整体运行或者在终端运行:新建pytest.ini并设置,使用pytest命令
pytest.ini
在这里插入图片描述

前后置处理
setup/teardown

在这里插入图片描述
模块级:(函数级同理)

def setup_module():
	print("准备测试数据")
def teardown_module():
	print("清理测试数据")
def test_douban1():
	pass
def test_douban2():
	pass
if __name__=='__main__':
	pytest.main()

类级:(方法级同理)

class TestDouban():
	def setup_class(self):
		print("准备测试数据")
	def teardown_class(self):
		print("清理测试数据")
	def test_douban1(self):
		pass
	def test_douban2(self):
		pass
if __name__=='__main__':
	pytest.main()
夹具fixture

参数scope: 默认function
(scope=“function”)每一个函数或方法都会调用
(scope=“class”) 每一个类调用一次
(scope=“module”) 每一个.py文件调用一次
(scope=“session”)是多个文件调用一次,.py文件就是module
参数autouse: 默认True,即所有方法都调用前置处理

@pytest.fixture(scope="function",autouse=False)
def func():
	print("前置步骤")
def test_douban1(func):
	pass
def test_douban2():
	pass
if __name__=='__main__':
	pytest.main()
parametrize参数化

传统:一个测试用例对应一个测试函数;
参数化:数据驱动 ,实现测试数据和测试函数分离,只需要一个测试函数,n个测试用例调用n次测试函数,生成n次测试结果
单参数:

    @pytest.mark.parametrize('num', [1, 2, 3])
    def test_login(self, num):
        print(f'当前测试用例数据:{num}')

多参数:

    @pytest.mark.parametrize('num, expect', [(1, True), (2, True), (3, False)])
    def test_login(self, num, expect):
        print(f'当前测试用例数据:{num}')
        assert func(num) == expect
数据库参数化

config.ini内容:

# 数据库配置文件,多种类型存储数据
[dbinfo]
dbinfo ={
    "host":"...",
    "user":"...",
    "password":"...",
    "port":3306
    }

封装数据库连接操作:
使用pymysql基本步骤:

  • 创建连接对象db
  • 创建游标对象cur
  • 执行sql
  • 关闭游标、连接对象
class ReadFile():
    def __init__(self):
        self.conf = ConfigParser(strict=False)  # 防止读取重复配置项
        self.conf.read('config.ini', encoding='utf-8')

    # 封装读取config配置文件
    def getValue(self, section='host', option='session_host'):
        value = self.conf.get(section, option)
        return value


class Connect():
    def __init__(self, database='dushuwu'):
        self.dbinfo = eval(ReadFile().getValue('dbinfo', 'dbinfo'))
        # print(dbinfo)
        # 数据库连接对象
        self.db = pymysql.Connect(
            database=database,
            **self.dbinfo,
            cursorclass=pymysql.cursors.DictCursor
        )
        # 定义游标
        self.cur = self.db.cursor()


if __name__ == '__main__':
    if __name__ == '__main__':
    con = Connect()
    sql = 'select book_name from book where id=134'
    con.cur.execute(sql)

    data = con.cur.fetchall() #获取数据

    con.cur.close()
    con.db.close()
    print(f'从数据库中获取的数据:{data}')
示例代码:

参考博客

import os.path
import pandas as pd
import pytest
import yaml
import json
import codecs
from selenium import webdriver
from selenium.common import TimeoutException, NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


@pytest.fixture(scope="module")
def func():
    print("前置操作")
    driver = webdriver.Firefox()  # 选择火狐浏览器
    driver.implicitly_wait(10)
    # driver.maximize_window()
    return driver


# 读取yaml文件的方法
def get_caseData():
    return_value = []
    data_file_path = os.path.dirname(os.path.abspath(__file__)) + '/cases.yaml'
    print(data_file_path)
    _is_yaml_file = data_file_path.endswith((".yml", ".yaml"))
    with codecs.open(data_file_path, 'r', 'utf-8') as f:
        # 从YAML或JSON文件中加载数据
        if _is_yaml_file:
            data = yaml.safe_load(f)
        else:
            data = json.load(f)
    for i, elem in enumerate(data):
        if isinstance(data, dict):
            key, value = elem, data[elem]
            if isinstance(value, dict):
                case_data = []
                for v in value.values():
                    case_data.append(v)
                return_value.append(tuple(case_data))
            else:
                return_value.append((value,))  # 单元素的元组
    return return_value  # 返回结果[('byhy', 'sdfsdf'), ('byhy1', 'sdfsdf1')]


# 读取excel
def get_excelData():
    excel_file = os.path.dirname(os.path.abspath(__file__)) + r'/test_p_readYE_searchName.xlsx'
    if not os.path.exists(excel_file):
        raise ValueError("File not exists")
    # 初始化
    s = pd.ExcelFile(excel_file)
    # 解析Excel Sheet
    df = s.parse('Sheet1')
    # 以list格式返回数据
    return df.values.tolist()


# def get_dfData():
#     xls = pd.ExcelFile(r'../data/test_p_readConfig_searchName.xlsx', engine='openpyxl')
#     xls1 = xls.parse(xls.sheet_names[0])
#     df = xls1.to_dict('records')
#     return df


# @pytest.mark.parametrize("username,password", get_caseData())  # 读取yaml
@pytest.mark.parametrize("search_word", get_excelData()) #读取excel
# def test_param(username, password, func):
def test_param(search_word, func):
    # print(f'获取的yaml_username数据:{username}')
    # print(f'获取的yaml_password数据:{password}')
    print(f'获取的excel_searchWord数据:{search_word}')
    driver = func
    driver.get('http://localhost/sign.html')
    driver.find_element(By.XPATH, '//*[@id="username"]').clear()
    # driver.find_element(By.XPATH, '//*[@id="username"]').send_keys(username)
    # driver.find_element(By.XPATH, '//*[@id="password"]').send_keys(password)
    driver.find_element(By.XPATH, '//*[@id="username"]').send_keys('byhy')
    driver.find_element(By.XPATH, '//*[@id="password"]').send_keys('sdfsdf')
    driver.find_element(By.XPATH, '/html/body/div/div[2]/div/div[1]/div[3]/div/button').click()
    try:
        WebDriverWait(driver, 20).until(
            EC.visibility_of_element_located((By.XPATH, '/html/body/div/aside/a'))
        )
    except TimeoutException:
        print("Timeout occurred while waiting for the element to be visible.")
    except NoSuchElementException:
        print("Element was not found on the page.")
    driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').clear()
    # driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').send_keys('sz_001200')
    driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/input').send_keys(search_word)
    driver.find_element(By.XPATH, '/html/body/div/div[1]/div[2]/div[2]/div/span/button').click()


def teardown_function():
    print("后置操作")


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

jenkins持续集成

jenkins框架部署自动化测试学习:B站视频
通过war包运行jenkins:war包目录下输入

java -jar jenkins.war --httpPort=8080

当看到 Jenkins is fully up and running 就表示 jenkins 已经启动完成了
jenkins配置:参考博客
整体步骤:

  • 将上传到gitlab的项目url关联到jenkins项目中
  • jenkins项目配置,主要是定时构建,构建后发送邮件

代码

整体框架1

import pytest
from xToolkit import xfile
#一行代码读取所有的测试用例信息
测试用例集=xfile.read("接口测试用例.xls").excel_to_dict(sheet=1)
print(测试用例集)
@pytest.mark.parametrize"case_info",测式用例集)
def test_case_exec(case_info)#这个方法--专门用于执行测试用例
print"模拟-发起接口请求:",case_info)
#调用pytest框架
if __name__=='main':
	pytest.main(['-s',
				'-v',
				'--capture=sys', #保留系统输出
				'api_pytest_framework.py',
				'--clean-alluredir',
				'--alluredir=allure-results'])#生成测试数据-用于生成htmL报告
#结合aLLure生成测试报告
os.system(r"allure generate -c-o 测试报告")

整体框架2

excel填写测试用例:在这里插入图片描述

import pytest
import pandas as pd
import requests
import json

# 使用ExcelFile读取文件
xls = pd.ExcelFile(r'Pytest_case.xlsx')

# 尝试读取第一个工作表
df = xls.parse(xls.sheet_names[0])
df = df.to_dict('records')
# print("读取到df的值:", df)

@pytest.mark.parametrize("case_info",df)
def test_case_exec(case_info):
    # print("每个case_info的值:", case_info)
    响应 = requests.request(url=case_info['接口URL'],
                        method=case_info['请求方式'],
                        headers=json.loads(case_info['URL参数']),
                        json=json.loads(case_info['JSON参数']) if case_info['请求方式'] == 'post' else None
                        )
    print(响应.json())
    # assert 响应.status_code == case_info['预期状态码'],'响应状态码不匹配'

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

在python代码中读取excel

excel中数据如下:
在这里插入图片描述

# 使用pandas的ExcelFile读取文件
xls = pd.ExcelFile(r'Pytest_case.xlsx')
# 读取第一个工作表
df = xls.parse(xls.sheet_names[0])
# 将DataFrame转换为字典类型
df = df.to_dict('records')

request发起请求

与postman

三种参数格式:params,data,json对应的postman中的参数位置
在这里插入图片描述

与java代码

第一种params,代码格式如下:

在这里插入图片描述

postman中设置参数如下:
在这里插入图片描述

第二种data,代码格式如下:
在这里插入图片描述

postman中设置参数如下:
在这里插入图片描述

第三种json,代码格式如下:
在这里插入图片描述

postman中设置参数如下:
在这里插入图片描述

书写格式
# pytest参数化机制多用例执行
@pytest.mark.parametrize("case_info",测试用例集)
def test_case_exec(case_info):#这个方法--专门用于执行测试用例
	#发起接口请求
	print"发起接口请求:",case_info)
	接口响应=requests.request(
		url=case_info['接口URL'],
		method=case_info['请求方式'],
		params=eval(case_info['URL参数']),
		data=case_info['表单参数'],
		json=case_info['JSON参数']
	)
	print("接口响应内容:",接口响应.text)

断言

常用断言类型
在这里插入图片描述

首先需要在excel中添加预期状态列

#判定接口测试结果[断言]
assert 接口响应.status_code==case_info['预期状态码'],'响应状态码不匹配'

提取参数

需要在excel中预先填写提取参数变量名($…直接匹配所有元素),然后获取值并赋给字典dic的一个键,这个键的名称由case_info[“提取参数”]的值决定。

if case_info["提取参数"]:# 不为空true执行下面代码
	t_lst=jsonpath.jsonpath(应答.json(),"$.." + case_info["提取参数"])
	#dic.update({case_info["提取参数"],t_lst[o]})
	dic[case_info["提取参数"]]=t_lst[0]

使用提取的参数

修改excel中的url格式(添加参数)
在这里插入图片描述
代码中将url中的请求参数替换掉

url=case_info["接口URL"]
if "$" in url:
	url=Template(url).substitute(dic)

定制测试报告

定制测试报告标题、描述等信息

import pytest,os,requests,allure
def test_case_exec(case_info)#这个方法--专门用于执行测试用例
	#把测试报告结果--和测试用例信息关联起来alLure测试报告动态定制化		
	allure.dynamic.title(case_info['用例编号'])
	allure.dynamic.description(case_info['描述'])

学习视频

B站:Pytest基础知识

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值