Python+Selenium+unittest脚本公共代码整理

1、日志模块

# 导入日志模块
import logging
# 日志文件输出
from logging.handlers import RotatingFileHandler
import time
# 日志存在目录
from Common import dir_config

# 设置日志格式  
"""
asctime:字符串形式的当前时间
levelname:文本形式的日志级别
filename:调用函数所在模块的文件名称
funcName:日志输出的函数名
lineno:函数语句执行所在代码行
message:用户输出信息
"""
log_fmt = " %(asctime)s %(levelname)s %(filename)s %(funcName)s [line:%(lineno)d] %(message)s "
"""
%a:本地星期的简称
%d:一个月中的第几天
%b:本地月份的简称
%Y:年份的完整表达形式
%H:24小时制的小时
%M:分钟
%S:秒数
"""
date_fmt = " %a, %d %b %Y %H:%M:%S "
# 获取当前时间,且将其格式转换成字符串(strftime将时间格式转换成字符串显示;strptime将字符串转换成时间格式显示)
cur_time = time.strftime("%Y-%m-%d %H%M", time.localtime())
# 将日志信息输出到Stream
handler_1 = logging.StreamHandler()
# 将日志信息输出到磁盘文件,且设置其文件名称
handler_2 = RotatingFileHandler(
    dir_config.logs_path + "/Web_Autotest_{0}.log".format(cur_time),
    backupCount=20,
    encoding='utf-8'
)
# 设置日志输出格式
logging.basicConfig(
    format=log_fmt,
    datefmt=date_fmt,
    level=logging.INFO,
    handlers=[handler_1, handler_2]
)

2、文件存储

# 导入os模块
import os

# 根目录 
base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
logs_path = os.path.join(base_path, "Outputs\logs")
reports_path = os.path.join(base_path, "Outputs\\reports")
screenshots_path = os.path.join(base_path, "Outputs\screenshots")
Testcases_path = os.path.join(base_path, "Testcases")
Testdatas_path = os.path.join(base_path, "Testdatas")

# os.path.realpath(__file__) 当前文件的绝对路径
# os.path.dirname() 当前文件所属的文件夹,可嵌套使用
# os.path.join(base_path, "Testcases") 添加文件

3、Common封装

from selenium.webdriver.remote.webdriver import WebDriver
# 导入时间等待
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
# expected_conditions模块包含一系列可用于判断的条件
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
# 导入日历
import calendar as cal
# 导入时间
import time
# 导入日期
import datetime
# 模拟键盘操作(Python自带的插件)
import win32api
# 控制键盘(Python自带的插件)
import win32con
# 脚本字段的复制、粘贴
import pyperclip
# 导入日志模块
from Common import logger_fun
# 导入问价目录
from Common.dir_config import screenshots_path
class BasePage:
	"""
	包含了PageObjects当中,用到所有的selenium底层方法
	还可以包含通用的一些元素操作,如alert,iframe,windows...
	实现日志记录、实现失败截图
	"""
	# 初始化驱动
    def __init__(self, driver: WebDriver):
        self.driver = driver
        
    # 等待元素可见(该方法不单独使用,由其他方法调用)
    def wait_elevisible(self, loc, timeout=120, frequency=0.5, doc="" ):
        """
        :param loc:要等待的元素定位(必传)
        :param timeout:超时时间
        :param frequency:轮询周期
        :param doc:备注
        :return:
        """
        # 计算程序执行的时间(系统时间的时间戳)
        start_time = time.time()
        try:
            WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
        except:
			# 模块打印异常
            logger_fun.logging.exception("等待{}元素可见超时".format(loc))
            # 失败截图(调用自定义函数:见下面)
            self.do_save_screenshot(doc)
            raise
        else:
            end_time = time.time()
            # 两次调用的时间差即为系统经过的总时间
            duration = end_time - start_time
            # 日志输出
            logger_fun.logging.info("等待{}元素可见,耗时{}".format(loc, duration))
            
    # 查找元素:查找某个元素(该方法不单独使用,由其他方法调用)
    def get_element(self, loc, doc=""):
        """
        :param loc:要查找元素的元素定位(必传)
        :param doc:备注
        :return:
        """
        try:
            # find_element系统自带的查找元素,*loc不定长参数
            ele = self.driver.find_element(*loc)
        except:
            logger_fun.logging.exception("查找{}元素存在,失败!".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("查找{}的元素{}成功。".format(doc, loc))
            return ele
            
    # 页面截图:(该方法不单独使用,由其他方法调用)
    def do_save_screenshot(self, doc=""):
        """
        :param doc:备注
        :return:
        """
        # 获取本地当前时间,且将其时间格式转换为字符串格式
        cur_time = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S')
        # 设置文件的存储路径及文件名称
        file = screenshots_path+"/{}_{}.png".format(doc, cur_time)
        try:
        	# 调用系统自带的截图函数
            self.driver.save_screenshot(file)
        except:
            logger_fun.logging.exception("网页截图操作失败")
        else:
            logger_fun.logging.info("截图成功,存储路径为:{0}".format(file))

    # 查找元素是否显示:返回布尔值(存在返回True 不存在返回False)
    def is_element_present(self, loc, timeout=10, frequency=0.5, doc=""):
        """
        :param loc:要查找元素的元素定位(必传)
        :param timeout:超时时间
        :param frequency:轮询周期(多久查看一下元素是否在页面中出现)
        :param doc:备注
        :return:
        """
        time.sleep(0.5)
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # is_displayed:自带的函数判断元素是否对用户可见
            ele.is_displayed()
        except:
            logger_fun.logging.exception("{}元素不可见".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("{}元素可见".format(loc))

    # 输入框输入文本:
    def input_text(self, loc, value, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:文本输入框的输入框元素定位(必传)
        :param value:输入的值(必传)
        :param timeout:超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # 模拟输入元素send_keys
            ele.send_keys(value)
        except:
            logger_fun.logging.exception("向{}元素输入{}失败".format(loc, value))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("向{}元素输入{}成功".format(loc, value))
            
    # 清空文本输入框内容:
    def clear_text(self, loc, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:要清空文本输入框的输入框元素定位(必传)
        :param timeout:超时时间
        :param frequency:轮询周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # 如果文本是文本输入文本,则清除文本信息
            ele.clear()
        except:
            logger_fun.logging.exception("清除{}内容失败".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("清除{}内容成功".format(loc))

    # 点击:场景需要点击某个元素时使用的
    # “->”符号表示返回值注解符号,通过允许将元数据附加到描述其参数和返回值的函数来扩展其功能
    def click(self, loc: object, timeout: object = 60, frequency: object = 0.5, doc: object = "") -> object:
        """
        :param loc:要点击元素的元素定位(必传)
        :param timeout:超时时间
        :param frequency:轮询周期
        :param doc:备注
        :return:
        """       
        time.sleep(0.5)
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # 系统自带点击函数
            ele.click()
        except:
            logger_fun.logging.exception("向{}元素点击失败".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("向{}元素点击成功".format(loc))
            
    # 获取元素文本值:获取页面中单个元素的文本值
    def get_element_text(self, loc, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:要获取元素文本值的元素定位(必传)
        :param timeout:超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # 获取元素文本值
            text = ele.text
        except:
            logger_fun.logging.exception("获取{}元素文本值失败".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("获取{}元素文本值成功".format(loc))
            return text

    # 获取元素属性值:
    def get_element_attribute(self, loc, attr, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:要获取元素属性值的元素定位(必传)
        :param attr:要获取元素属性值的元素属性名(必传)
        :param timeout:超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
            # 获取元素属性
            value = ele.get_attribute(attr)
        except:
            logger_fun.logging.exception("获取{}元素属性值失败".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("获取{}元素属性值成功".format(loc))
            return value

    # 获取列表数据长度:列表中没有“共XX条”汇总数据;需要从表格中获取一共有多少条数据;返回整数类型
    def get_list_length(self, loc, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:要获取元素数量的元素定位
        :param timeout:超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        #查找元素
        ele = self.get_elements(loc, doc)
        try:
            # 获取元素列表数据长度
            value = len(ele)
        except:
            logger_fun.logging.exception("获取{}元素属性值失败".format(loc))
            self.do_save_screenshot(doc)
            return 0
        else:
            logger_fun.logging.info("获取{}元素属性值成功".format(loc))
            return value
            
    # 获取列表数据单列内容
    def get_list_single_column(self, table_tbody, j, doc=''):
        """
        :param table_tbody: 表格定位路径
        :param j:获取第几列元素
        :param doc:备注信息
        :return:将列内容返回至列表中
        """
        # 定位表格路径
        element = self.driver.find_element_by_xpath(table_tbody)
        # 获取每一行的数据tr
        table_tr_list = element.find_elements_by_tag_name("tr")
        time.sleep(1)
        table_list = []
        # 获取列表数据
        total_numbers = len(table_tr_list)
        if total_numbers >= 1:
            for i in range(1, total_numbers + 1):
                table_td_list = self.driver.find_elements_by_xpath(table_tbody + '//tr[' + str(i) + ']//td[' + str(j) + ']')[0]
                # 获取其文本信息
                table_td_list1 = table_td_list.get_attribute('textContent')
                table_list.append(table_td_list1)
            # 去除空白字符
            new_table_list = [x.strip() for x in table_list if x.strip() != '']
            return new_table_list
        else:
            ret_info = "列表数据为空"
            return ret_info

    # 获取列表页面全部数据
    def get_list_all_datas(self, table_tbody):
        time.sleep(1)
        list_1 = []
        # 定位表格路径
        element = self.driver.find_element_by_xpath(table_tbody)
        # 获取每一行数据tr
        table_tr_list = element.find_elements_by_tag_name("tr")
        # 按行查询表格的数据,取出的数据是一整行
        for tr in table_tr_list:
            # tr.text获取表格每行的文本内容、切割字符串
            list_2 = (tr.text).split()
            list_1.append(list_2)
        if list_1 != []:
            return list_1
        else:
            ret_info = "列表数据为空"
            return ret_info

    # 切换窗口/页面:应用场景:点击按钮等操作出现一个新的页面,需要操作新页面中的元素时要先切换到新页面中。最新出现的窗口下标为-1
    def switch_window(self, doc=""):
        """
        :param doc: 备注
        :return:
        """
        try:
            # 获取所有的window列表
            windows = self.driver.window_handles
            #切换到最新窗口
            self.driver.switch_to.window(windows[-1])
        except:
            logger_fun.logging.exception("切换窗口失败")
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("切换窗口成功")

    # 获取当前日期:返回当前日期,格式为年月日
    def get_date(self):
        return time.strftime("%Y-%m-%d", time.localtime(time.time()))

    # 获取元素默认值:
    def get_default_value(self, loc, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:获取元素的元素定位(必传)
        :param timeout: 超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        #  查找元素
        ele = self.get_element(loc, doc)
        try:
        	# 获取属性默认值
            default_value = ele.get_attribute('value')
        except:
            logger_fun.logging.exception("获取{}元素文本值失败".format(loc))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("获取{}元素文本值成功".format(loc))
            return default_value
            
	# 通过id切换iframe:
    def switch_iframe_by_id(self, id, doc=""):
        self.driver.switch_to.frame(self.driver.find_element_by_id(id))
  
  	# 通过xpath切换iframe:
    def switch_iframe_by_xpath(self, xpath, doc=""):
        self.driver.switch_to.frame(self.driver.find_element_by_xpath(xpath))      
 
    # 通过element切换iframe:
    def switch_iframe_by_element(self, element, doc=""):
        self.driver.switch_to.frame(self.driver.find_element(*element))

    # 输入框输入文本形式上传文件(前提:标签名为input且type="file")
    def input_text_uploadfile(self, loc, value, timeout=60, frequency=0.5, doc=""):
        """
        :param loc:要上传文件的上传按钮元素定位(必传)
        :param value:要上传的文件名(必传:绝对路径)
        :param timeout:超时时间
        :param frequency:轮循周期
        :param doc:备注
        :return:
        """
        # 元素可见
        self.wait_elevisible(loc, timeout, frequency, doc)
        # 查找元素
        ele = self.get_element(loc, doc)
        try:
        	# 直接上传文件的绝对路径(基本上同文本输入)
            ele.send_keys(value)
        except:
            logger_fun.logging.exception("向{}元素输入{}失败".format(loc, value))
            self.do_save_screenshot(doc)
            raise
        else:
            logger_fun.logging.info("向{}元素输入{}成功".format(loc, value))
            
    # 非input标签上传文件
    def isnot_input_uploadfile(self, eleLoc, filePath, doc=''):
        """
        使用python的win32api,win32con模拟按键输入,实现文件上传操作
        :param eleLoc: 页面中的上传文件按钮
        :param filePath: 要上传的文件地址(绝对路径)
        :param doc: 备注信息
        :return:
        """
        try:
            # 复制文件路径到剪切板
            pyperclip.copy(filePath)
            self.click(eleLoc)
            time.sleep(3)
            # 发送 ctrl(17) + V(86)按钮(将剪切的内容粘贴)
            win32api.keybd_event(17, 0, 0, 0)
            win32api.keybd_event(86, 0, 0, 0)
            # 松开按键
            win32api.keybd_event(86, 0, win32con.KEYEVENTF_KEYUP, 0)
            win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0)
            time.sleep(1)
            # 点击回车键
            win32api.keybd_event(13, 0, 0, 0)
            win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0)
        except:
            time.sleep(1)
            logger_fun.logging.info("向{}元素上传文件失败".format(eleLoc))
            self.do_save_screenshot(eleLoc)
            raise
        else:
            time.sleep(1)
            logger_fun.logging.info("向{}元素上传文件成功".format(eleLoc))

    # 移动到某元素上:当元素定位无法定位到唯一值时,需要先移动到相近位置的某个元素上,才能对指定元素进行操作
    def move_to_element(self, loc, Index=1, timeout=120, frequency=0.5, doc=""):
        """
        :param loc: 要移动到某个元素的元素定位(必传)
        :param Index: 元素下标
        :param timeout: 超时时间
        :param frequency: 轮循周期
        :param doc: 备注
        :return:
        """
        ac = ActionChains(self.driver)
        # 找到要操作的元素
        ele = self.get_elements(loc, doc)[Index]
        # 鼠标悬浮
        ac.move_to_element(ele)
        ac.perform()

4、main函数

from Common.dir_config import logs_path, screenshots_path, Testcases_path, reports_path
# 导入测试报告BeautifulReport 模块
from BeautifulReport import BeautifulReport
# 导入unittest模块
import unittest
# 导入删除日志、测试报告自定义模块
from Common.delete_log import DeleteLogFile as DF

"""Windows系统"""
# 删除目录下的日志文件、测试报告文件
Del_log = DF(r"{}".format(logs_path), r"{}".format(screenshots_path))
Del_log.delete_file()

if __name__ == '__main__':
    # 批量调用测试用例
    suite_tests = unittest.defaultTestLoader.discover(r"{}".format(Testcases_path))
    BeautifulReport(suite_tests).report(
        filename='Auto_Testing_01自动化测试报告',
        description='Auto_Testing_01自动化测试报告',
        report_dir=r'{}'.format(reports_path)
    )

5、删除日志、测试报告

import os
class DeleteLogFile:
	# 构造初始化函数
    def __init__(self, path_1, path_2):
        self.path_1 = path_1
        self.path_2 = path_2
    def delete_file(self):
        for path in [self.path_1, self.path_2]:
        	# 获取path路径下的全部文件
            all_log_files = os.listdir(path)
            # 对列表文件进行排序
            all_log_files.sort()
            for num in range(len(all_log_files)):
            	# 批量删除文件目录
                os.remove(os.path.join(path,all_log_files[num]))

6、测试用例

import unittest
from selenium import webdriver
import time

class TestLogin(unittest.TestCase):
    # setUp():每个测试方法运行前进行(测试前初始化工作,一条用例执行一次,若N次用例就需要执行N次)
    # setUpclass():所有的测试方法运行前运行,为单元测试做前期准备,但必用@classmethod装饰器修饰,整个测试过程中只执行一次
    # tearDown():每个测试方法运行结束后运行,测试后的清理工作。一条用例执行一次,若N次用例就执行N次。
    # tearDownClass():所有的测试方法运行结束后运行,为单元测试做后期清理工作,但必须使用@classmethod装饰器进行修饰,整个测试过程中只执行一次
    # 测试开始前创建浏览器
    # @classmethod
    def setUp(self):
        executable_path = r"G:\BaiduNetdiskDownload\chromedriver.exe"
        self.driver = webdriver.Chrome(executable_path)
        self.driver.get(GD.login_url_mainframe)
        self.driver.maximize_window()

    def tearDown(self):
        self.driver.quit()
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值