admin 管理员组

文章数量: 887006

自动化接口测试python+selenium(request)+pytest+allure

注:一下都是利用python package创建

文件目录格式

class_test__init__.pydata__init__.pylogin.yamldata_read__init__.pyget_data.pyLogs__init__.pyerr.loglog.logpage_object__init__.pylogin_page.pytest_cases__init__.pytest_case_demo.pyUtils__init__.pyLog.pyAssert.pychromedriverMyConfig.py

使用pycharm创建项目class_test,在该项目创建data文件,在data文件中创建logo.yaml文件

- user: 12345678pwd: 111111text: 张三

创建data_read文件,该文件下创建get_data.py文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 17/03/2022 14:12
# @Author  : ducker
# @Site    : 
# @File    : get_data.py
# @EN      : 数据读取
# @Software: PyCharm
import yamlclass GetData:# 获取数据文件中的内容def get(self,path):file = open(path,'r',encoding='utf-8')data = yaml.load(file, yaml.FullLoader)return data# if __name__ == '__main__':
#     gd = GetData()
#     data = gd.get('../data/login.yaml')
#     print(data)

创建Log文件,在该文件下创建err.log和log.log文件

创建page_object文件,在该文件下创建login_page.py文件,下面是利用selenium,需要自己改为request模式

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 17/03/2022 13:22
# @Author  : ducker
# @Site    : 
# @File    : login_page.py
# @EN      : 中文
# @Software: PyCharm"""
登录页面对象,用于封装登录页要进行的所有的操作行为
"""
# from base.base_page import BasePage
from time import sleep
from selenium import webdriver
from selenium.webdrivermon.by import By
from class_test.base.base_page import BasePageclass LoinPage(BasePage):# urlurl = '/'# 登录-页面元素username = (By.NAME, 'username')pwd = (By.NAME,'password')button = (By.XPATH,'//*[@id="app"]/div/form/button')spans = (By.XPATH, '//span[@class="user-name"]')# 名片管理-页面元素card_button = (By.XPATH,'//*[@id="app"]/div/div[1]/div/div[1]/div/ul/div[2]/li/div')card_list_button = (By.XPATH,'//*[@id="app"]/div/div[1]/div/div[1]/div/ul/div[2]/li/ul/div[1]/a/li')card_list_button_department = (By.XPATH,'//*[@id="app"]/div/div[2]/section/div/div[2]/div/form/div[1]/div/div/div/input')card_list_button_name = (By.XPATH,'//*[@id="app"]/div/div[2]/section/div/div[2]/div/form/div[2]/div/div/input')card_list_button_name_text = (By.XPATH,'//*[@id="app"]/div/div[2]/section/div/div[2]/div/form/div[2]/div/div/input')card_list_input_search_name = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[2]/div/form/div[2]/div/div/input')card_list_search_query = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[2]/div/form/div[3]/div/button[1]')card_list_search_query_end = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[3]/div[3]/table/tbody/tr[1]/td[1]')# 登录业务实现def login(self, text, pwd):self.open()self.clear(self.username)self.input(self.username,text)self.clear(self.pwd)self.input(self.pwd, pwd)self.click(self.button)return self.get_text(self.spans).text# 名片管理def card_list(self,name,number):self.click(self.card_button)self.click(self.card_list_button)self.click(self.card_list_button_department)self.click(self.card_list_button_name)card_list_name_text = self.get_text(self.card_list_button_name_text)self.input(self.card_list_input_search_name,name)self.click(self.card_list_search_query)card_list_search_query_ends = self.get_text(self.card_list_search_query_end).textself.clear(self.card_list_input_search_name)self.input(self.card_list_input_search_name, number)self.click(self.card_list_search_query)card_list_search_query_number_ends = self.get_text(self.card_list_search_query_end).textreturn card_list_name_text,card_list_search_query_ends,card_list_search_query_number_ends# # 测试
if __name__ == '__main__':driver = webdriver.Chrome('../chromedriver')text = '1356781234'pwd = '111111'lp = LoinPage(driver)lp.login(text, pwd)# lp.get_texts()

在创建test_case文件,在该文件下创建test_case_demo.py文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 19/03/2022 16:28
# @Author  : ducker
# @Site    : 
# @File    : test_case_demo.py
# @EN      : 中文
# @Software: PyCharm
import osimport allure
from time import sleep
from ddt import ddt,file_data
from class_test_.data_read.get_data import GetData
import pytest
from selenium import webdriver
from class_test_.page_object.login_page import LoinPage
from class_test_ import Assert
from class_test_.Utils import Logclass TestCases:# def __init__(self):#     self.asser = Assert.Assert()def setup_class(self):self._assert = Assert.Assert()@allure.feature("首页测试类")# @allure.severity('blocker')# @allure.story('切换菜单栏')# @allure.description('菜单栏切换')# @allure.link('')# @allure.issue('BUG编号:qa_001')# @allure.testcase('验证菜单栏切换数据是否正常')@pytest.mark.parametrize('data', GetData().get('../data/login.yaml'))# 测试用例def test_login(self, data):driver = webdriver.Chrome('../chromedriver')lp = LoinPage(driver)fact = lp.login(data['user'], data['pwd'])# print(lp.get_texts())sleep(3)# 获取你需要断言内容,定义为实际结果 factself._assert.assert_body(fact,data['text'])# assert fact == data['text'] , '实际结果是:{}'.format(data['text'])return lp@pytest.mark.parametrize('data', GetData().get('../data/login.yaml'))def test_card_list(self,data):print(data)# fact = self.test_login().card_list('李苗苗','1356781234')# self._assert.assert_body(fact[0], data['card_search_name'])# self._assert.assert_body(fact[1], data['card_list_search_query_ends'])# self._assert.assert_body(fact[2], data['card_list_search_query_number_ends'])# # # 获取你需要断言内容,定义为实际结果 fact# pytest.assume(fact[0] == data['card_search_name'], '实际结果是:{}'.format(data['card_search_name']))# pytest.assume(fact[1] == data['card_list_search_query_ends'], '实际结果是:{}'.format(data['card_list_search_query_ends']))# pytest.assume(fact[2] == data['card_list_search_query_number_ends'], '实际结果是:{}'.format(data['card_list_search_query_number_ends']))if __name__ == '__main__':# pytest.main(['-s','--html=report/report.html'])# pytest.main(['-s','v','--alluredir','/Users/super/PycharmProjects/cardtest/class_test_/reports/result'])# pytest.main([ '--alluredir', 'report/result', 'test_case_demo.py'])# # Log.MyLog.info()# allure_cmd = "allure generate ./report/result -o ./report/html --clean"  # 将报告转换成html格式文件的命令# # split = 'allure' + 'generate' + './report/result' + '-o' + './report/html' + '--clean'# os.system(allure_cmd)# p = os.popen(allure_cmd, mode="r")  # 运行命令# print(p.read())  # 打印查看结果pytest.main(['-s', '-q', 'test_case_demo.py', '--alluredir', './report/xml'])os.system("allure generate ./report/xml -o  ./report/html ")

在创建Utils文件,在该文件下创建Log.py文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 19/03/2022 20:21
# @Author  : ducker
# @Site    : 
# @File    : asser_log.py
# @EN      : 断言日志封装
# @Software: PyCharmimport logging
import os
import time# 获取当前脚步文件父类的绝对路径(项目主目录)
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
log_file = path + '/Logs/log.log'
err_file = path + '/Logs/err.log'
date = '%Y-%m-%d %H:%M:%S'LEVELS = {'debug': logging.DEBUG,'info': logging.INFO,'warning': logging.WARNING,'critical': logging.CRITICAL
}
# 初始化日志对象logger
logger = logging.getLogger()# 实例化日志信息输出到磁盘文件上到流,指定文件路径和编码
handler = logging.FileHandler(log_file, encoding='utf-8')
err_handler = logging.FileHandler(err_file, encoding='utf-8')def create_file(file):path = file[0:file.rfind('/')]if os.path.isdir(path) and os.path.isfile(path):return Falseif not os.path.isdir(path):os.makedirs(path)if not os.path.isfile(file):fd = open(file, mode='w', encoding='utf-8')fd.close()else:pass# 为logger添加写入对应的handler
def add_handler(levels):if levels == 'error':logger.addHandler(err_handler)logger.addHandler(handler)# 调用log记录日志后移除logger中的handler,防止出现重复日志信息
def remove_handler(levels):if levels == 'error':logger.removeHandler(err_handler)logger.removeHandler(handler)# 获取当前时间
def get_current_time():return time.strftime(date, time.localtime(time.time()))class MyLog:# 设置日志等级,设置默认日志等级notset所有等级logger.setLevel(LEVELS.get('debug', logging.NOTSET))# 创建日志文件create_file(log_file)create_file(err_file)def debug(self, log_msg):"""添加写入日志的流添加等级为debug的日志移除日志流:data log_msg::return:"""add_handler('debug')logger.debug("[DEBUG " + get_current_time() + "] {}".format(log_msg))remove_handler('debug')def warning(self, log_msg):add_handler('warning')logger.warning("[WARNING " + get_current_time() + "] {}".format(log_msg))remove_handler('WARNING')def error(self, log_msg):add_handler('error')logger.error("[ERROR " + get_current_time() + "] {}".format(log_msg))remove_handler('error')def info(self, log_msg):add_handler('info')logger.info("[INFO " + get_current_time() + "] {}".format(log_msg))remove_handler('info')def critical(self, log_msg):add_handler('critical')logger.critical("[CRITICAL " + get_current_time() + "] {}".format(log_msg))remove_handler('critical')if __name__ == '__main__':m = MyLog()m.info("哈哈哈")

在次创建Assert.py和MyConfig.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 21/03/2022 13:06
# @Author  : ducker
# @Site    : 
# @File    : Assert.py
# @EN      : 断言
# @Software: PyCharmfrom class_test_.Utils import Log
import jsonclass Assert:def __init__(self):self.log = Log.MyLog()def assert_status(self, code, assert_code):"""请求响应状态码断言:data code::data assert_code::return:"""try:assert code == assert_codereturn Trueexcept Exception:self.log.error("请求状态码对比失败,请求状态码为:{0},断言状态码为:{1}".format(code, assert_code))raisedef assert_body(self, body, assert_body):"""请求响应body断言:data body::data assert_body::return:"""try:assert body == assert_bodyreturn Trueexcept Exception:self.log.error("响应body对比失败,响应body为:{0},断言body为:{1}".format(body, assert_body))raisedef assert_in_body(self, body, assert_text):"""响应信息断言:data body::data assert_text::return:"""try:# 字段转换为字符串,ensure_ascii参数为输出是否为ASCII编码(序列化时中文默认使用ASCII编码)text = json.dumps(body, ensure_ascii=False)assert assert_text in textreturn Trueexcept Exception:self.log.error("断言字符串不在响应体中,断言字符串为:{0},响应信息为:{1}".format(assert_text, body))raisedef assert_time(self, time, assert_time):"""响应时间断言:data time::data assert_time::return:"""try:assert time < assert_timereturn Trueexcept Exception:self.log.error("响应时间超时,断言时间:{0},响应时间:{1}".format(assert_time, time))raise
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 21/03/2022 12:57
# @Author  : ducker
# @Site    : 
# @File    : config_log.py
# @EN      : 封装logging
# @Software: PyCharmfrom configparser import ConfigParser
from class_test_.Utils import Log
import osclass Config:# 标题TITLE = "test_env"TITLE_EMAIL = 'email'TITILE_DB = 'database'# 登录配置TESTER = 'tester'LOGIN_HOST = 'loginHost'LOGIN_INFO = 'loginInfo'LOGIN_HEADERS = 'headers'TOKEN = 'token'HOST = 'host'# 邮件配置SMTP_SERVER = 'smtpserver'SENDER = 'sender'RECEIVER = 'receiver'USERNAME = 'username'PASSWORD = 'password'# sql配置DB_DIR = 'host'DB_PORT = 'port'DB_USER = 'username'DB_PASSWORD = 'password'path_dir = str(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))'''os.pardir 获取当前目录的父目录字符串名称 '..'os.path.dirname(path)返回path的父路劲,__file__当前脚本运行的路劲os.path.join(path,name)连接目录与文件名os.path.abspath(name)返回脚本的绝对路劲'''def __init__(self):# 实例化类ConfigParser、MyLogself.config = ConfigParser()self.log = Log.MyLog()# 获取配置文件config.ini绝对路劲self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.ini')self.xml_report_path = Config.path_dir + '/Reports/xml'self.html_report_path = Config.path_dir + '/Reports/html'if not os.path.exists(self.config_path):raise FileNotFoundError("配置路径错误")self.config.read(self.config_path, encoding='utf-8')self.tester = self.get_config(Config.TITLE, Config.TESTER)self.login_host = self.get_config(Config.TITLE, Config.LOGIN_HOST)self.login_info = self.get_config(Config.TITLE, Config.LOGIN_INFO)self.login_headers = self.get_config(Config.TITLE, Config.LOGIN_HEADERS)self.token = self.get_config(Config.TITLE, Config.TOKEN)self.host = self.get_config(Config.TITLE, Config.HOST)# 邮件配置信息读取# self.smtpserver = self.get_config(MyConfig.TITLE_EMAIL, MyConfig.SMTP_SERVER)# self.sender = self.get_config(MyConfig.TITLE_EMAIL, MyConfig.SENDER)# self.receiver = self.get_config(MyConfig.TITLE_EMAIL, MyConfig.RECEIVER)# self.username = self.get_config(MyConfig.TITLE_EMAIL, MyConfig.USERNAME)# self.password = self.get_config(MyConfig.TITLE_EMAIL, MyConfig.PASSWORD)# sql配置信息读取self.sql_host = self.get_config(Config.TITILE_DB, Config.DB_DIR)self.sql_port = self.get_config(Config.TITILE_DB, Config.DB_PORT)self.sql_user = self.get_config(Config.TITILE_DB, Config.DB_USER)self.sql_password = self.get_config(Config.TITILE_DB, Config.DB_PASSWORD)def get_config(self, title, key):"""配置文件读取:data self::data title::data value::return:"""return self.config.get(title, key)def set_config(self, title, key, value):"""配置文件更新:data self::data title::data value::data text::return:"""self.config.set(title, key, value)with open(self.config_path, 'w') as f:return self.config.write(f)def add_config(self, title):"""配置文件title添加:data title::return:"""self.config.add_section(title)with open(self.config_path, 'w') as f:return self.config.write(f)

本文标签: 自动化接口测试pythonselenium(request)pytestallure