Python自动化测试系列[v1.0.0][高效自动化设计]

Python多线程应用于自动化测试

将多线程在测试巧妙地应用,确实会带来很多好处,并且这是充分利用机器资源执行高效率测试很好的方式

# -*- coding: utf-8 -*-
import threading
from time import ctime
import time
from selenium import webdriverdef test_search(browser, word):print("Start search at: %s\n" % ctime())print("Browser is: %s\n" % browser)if browser == 'ie':browser_driver = webdriver.Ie()elif browser == 'chrome':browser_driver = webdriver.Chrome()elif browser != 'ie' and browser != 'chrome':print("browser 参数非'ie 或 chrome'启动Fiefox")browser_driver = webdriver.Firefox()browser_driver.get("http://www.baidu.com")browser_driver.find_element_by_id("kw").send_keys(word)browser_driver.find_element_by_id("su").click()time.sleep(3)browser_driver.quit()if __name__ == '__main__':dicts = {'chrome':'__davieyang__','ie':'davieyang','davieyang':'davieyang__'}threads = []for browser, word in dicts.items():t = threading.Thread(target = test_search, args=(browser, word))threads.append(t)for t in range(len(dicts)):threads[t].start()for t in range(len(dicts)):threads[t].join()
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
from time import ctime
import multiprocessingdef start_browser(browser, time):if browser == "chrome":print("starting chrome browser now! %s" % ctime())  # 控制台打印当前时间 chrome_driver = webdriver.Chrome()chrome_driver.get("http://www.baidu.com")sleep(time)chrome_driver.quit()elif browser == "firefox":print("starting firefox browser now! %s" % ctime())  # 控制台打印当前时间fire_driver = webdriver.Firefox()fire_driver.get("http://www.baidu.com")sleep(time)fire_driver.quit()else: print("starting ie browser now! %s" %ctime())  # 控制台打印当前时间ie_driver = webdriver.Ie()ie_driver.get("http://www.baidu.com")sleep(time)ie_driver.quit()#  定义字典参数        
browser_dict = {"chrome": 3, "firefox": 4}
#  定义空List用于存储进程
start_browser_processing = []
#  循环字典Key-Value,创建进程并加入到List中
for browser, time in browser_dict.items():processing_browser = multiprocessing.Process(target = start_browser, args = (browser, time))start_browser_processing.append(processing_browser)if __name__ == '__main__':for processing_browser in range(len(browser_dict)):start_browser_processing[processing_browser].start()for processing_browser in range(len(browser_dict)):start_browser_processing[processing_browser].join()print(u"全部结束 %s" % ctime())

PO设计模式I

当我们以脚本的形式编写了大量的自动化测试代码后,很容易发现代码可读性太差了、任何一个测试脚本几乎毫无扩展能力或者说扩展起来只能修改边边角角、一旦被测系统的UI层发生变化对于测试代码的修改来说简直就是灾难性的,整个自动化工程几乎毫无结构可言,当面临这些问题的时候实际上已经失去了一部分自动化测试原有的职能

任何自动化测试团队在开展自动化测试这项活动的之前,必须考虑的是这项活动的ROI,自动化测试的本职是解放人力,基于前边所述的常见情况不仅仅其ROI非常低,也违背了自动化的初衷

基于这个大背景下,常规的自动化测试活动必须引入PO模式,它是一种自动化测试的设计模式,也是一种解耦的思想即:以页面为单位,将页面中的控件及控件的动作逐一提炼,从而在测试脚本中可以直接使用,其最终目的是使得元素定位表达式``页面对象``测试代码三者分离

而分离后的测试代码,具有极高的可复用性、可读性,可维护性,可扩展性,而这像极了面向对象思想更像极了Java里Spring的MVC模式和Django的MTV模式

定义基本页面类

在自动化测试活动中,每个页面都有其独特的地方,每个月面有都与其共性,为了达到PO模式的目的,首先需要考虑的是每个月面的共性,第一项需要做的就是定义页面的基本特性及活动,它是所有页面类的基类,是独立于所有实际页面之外的

实例代码

# encoding = utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleepclass Page():"""基础类,仅用于定义一些页面常规内容及方法后续各个页面类继承该类实现各自页面的特别内容及方法"""login_url = 'http://mail.163.com'  # 定义基类参数默认值,当调用时未提供该参数时使用def __init__(self, driver, base_url=login_url):self.base_url = base_urlself.driver = driverself.timeout = 30def target_page(self):return self.driver.current_url == self.base_url  #  判断当前打开的url与参数给的url是否一致def open(self, url):"""打开页面"""url = self.base_urlself.driver.get(url)print(self.driver.current_url)def find_element(self, *loc):return self.driver.find_element(*loc)

定义具体页面类

定义了基本页面类,接下来便是具体到每个页面,因为每个也面都有不同,定义具体页面类也是定义其不同,但凡有相同的都应该抽象到基本页面类中去定义

class LoginPage(Page):"""登陆页面,页面对象类"""url = '/'"""此处我们暂时称页面元素为对象,虽然本代码并非那么像对象,后续的框架中我们更高度的封装它便是个可操作的对象"""username_loc = (By.NAME, "email")  # 页面控件对象:输入用户名的input控件password_loc = (By.NAME, "password")  # 页面控件对象:输入密码的input控件submit_loc = (By.ID, "dologin")  # 页面控件对象:登陆按钮的button控件"""为每个页面元素对象封装其相对应的方法"""def input_username(self, username):self.find_element(*self.username_loc).send_keys(username)  # 输入用户名def input_password(self, password):self.find_element(*self.password_loc).send_keys(password)  # 输入密码def click_submitbutton(self):self.find_element(*self.submit_loc).click()  # 点击登陆按钮def user_login(driver, username, password):login_page = LoginPage(driver)login_page.open()sleep(5)driver.switch_to.frame(driver.find_element_by_xpath("//*[@id='loginDiv']/iframe"))login_page.input_username(username)sleep(3)login_page.input_password(password)sleep(3)login_page.click_submitbutton()

编写测试脚本

定义基本页面类是为了更快速的定义具体页面类,使得页面对象的共性可以复用,减少测试代码量;定义具体页面类实际上是扩展了基本页面类,相对于基本页面类而言它是独特的页面类;之后,便可以轻松的编写自动化测试脚本,脚本内容也变得简洁而清晰

def login_main163():try:driver = webdriver.Chrome()username = 'xxxxxx'  # 登陆邮箱需要的真实账号password = 'xxxxxx'  # 登陆邮箱需要的真实密码user_login(driver, username, password)  # 调用前面封装好的user_login方法sleep(3)  #  等待3秒driver.switch_to.default_content()  # 切换出iframeassert_string = driver.find_element_by_xpath("/html/body/div[1]/nav/div[1]/ul/li[1]/span[2]").textprint(assert_string)assert (assert_string == '收 信')  # 断言关键字finally:driver.quit()  # 关闭浏览器窗口if __name__ == '__main__':main()

PO设计模式II

任何自动化测试团队在开展自动化测试这项活动的之前,必须考虑的是这项活动的ROI,自动化测试的本职是解放人力,基于前边所述的常见情况不仅仅其ROI非常低,也违背了自动化的初衷

基于这个大背景下,常规的自动化测试活动必须引入PO模式,它是一种自动化测试的设计模式,也是一种解耦的思想即:以页面为单位,将页面中的控件及控件的动作逐一提炼,从而在测试脚本中可以直接使用,其最终目的是使得元素定位表达式 页面对象 测试代码三者分离

而分离后的测试代码,具有极高的可复用性、可读性,可维护性,可扩展性,而这像极了面向对象思想更像极了Java里Spring的MVC模式和Django的MTV模式

定义页面元素

自动化测试一切的根源都来自于页面元素,简单的说自动化测试就是让页面上的控件自己完成它的活动,自动化测试代码要控制这些控件,就首先要获取这些控件的位置,这里讲的不是元素定位的那些方式,而是应该如何处理这些元素定位表达式使其达到分离的效果

在自动化测试工程中新建一个诸如loginpage.ini的属性文件,当然这个文件最好是以页面为单位,每个页面独立分离,然后以控件名=定位方式>定位表达式的形式定义页面每个控件

[leadscloud_login]
input_user_name = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[1]/div/div/input
input_user_password = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[2]/div/div[1]/input
button_login = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[3]/div/button

公共类方法

定义公共类方法,用于解析前面的页面元素,获取元素定位,并返回页面元素对象

"""
用于解析配置文件,并获取页面元素定位表达式
"""
# -*- coding: utf-8 -*-
# @Time: 2019/2/26 11:43
# @Author : Yang DaWei
# @Project : LeadsCloudAutomation
# @FileName: ParseElementLocator.py
from configparser import ConfigParser
from time import sleep
from selenium.webdriver.support.wait import WebDriverWaitclass ParseConfigFile:"""初始化解析文件类"""def __init__(self, driver, page_element_locator):self.driver = driverself.cf = ConfigParser()self.cf.read(page_element_locator, encoding='utf-8')def get_items_section(self, section_name):"""获取配置文件中指定section下的所有option_name键值对,并以字典类型返回给调用者注意:使用self.cf.items(sectionName)此种方法获取到配置文件中的options内容均被转换成小写,如loginPage.frame将被转换成loginpage.frame"""options_dict = dict(self.cf.items(section_name))return options_dictdef get_option_value(self, section_name, option_name):"""获取指定section下的指定option的值"""value = self.cf.get(section_name, option_name)return valuedef get_element_location(self, section_name, option_name, timeout):"""获取页面元素定位表达式,并以元素对象的形式返回给调用者:param section_name::param option_name::param timeout::return:"""driver = self.driverlocation = self.get_option_value(section_name, option_name)location_type = location.split(">")[0]location_value = location.split(">")[1]print("读取到的定位类型为:" + location_type + "\t读取到的定位信息为:" + location_value)try:element = WebDriverWait(driver, timeout).until(lambda x: x.find_element(by=location_type,value=location_value))return elementexcept Exception as e:print("定位元素超过" + str(timeout) + "秒,详细异常信息入下:")raise edef highlight_element(self, driver, element):"""调用JS,用于高亮控件:param driver::param element::return:"""driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", element,"background: yellow; border:2px solid red;")if __name__ == '__main__':from selenium import webdriver# from Config import VarConfigfrom PageObject.Login_Page import Login_Page# locator_file = VarConfig.login_pagedriver = webdriver.Firefox()driver.get("http://test.xxxxxx.com/Front-Vue/#/login")sleep(5)Login_Page(driver).input_user_name().send_keys("yangdawei_10171")# ge = ParseConfigFile(driver, locator_file)# input_user_name = ge.get_element_location("leadscloud_login", "input_user_name", 5)# ge.highlight_element(driver, input_user_name)Login_Page(driver).input_user_password().send_keys("yangdawei_10166")# input_user_password = ge.get_element_location("leadscloud_login", "input_user_password", 5)# ge.highlight_element(driver, input_user_password)# input_user_password.send_keys("111111")# button_login = ge.get_element_location("leadscloud_login", "button_login", 5)# ge.highlight_element(driver, button_login)Login_Page(driver).login_button().click()driver.quit()

页面元素对象封装

通过公共类方法,读取页面元素定位信息,让后以页面控件为单位封装成可操作的对象

'''用于获取PageElementLocator.ini 中Login页面元素对象'''
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from Util.GetElements import *
from Util.ParseElementLocator import ParseConfigFileclass LoginPage(object):def __init__(self, driver):self.driver = driverself.parseCF = ParseConfigFile()self.loginOptions = self.parseCF.getItemsSection("163mail_login")print(self.loginOptions)def switchtoframe(self):try:# 从定位表达式配置文件中读取frame的定位表达式locatorExpression = self.loginOptions["loginPage.frame".lower()].split('>')[1]self.driver.switch_to.frame(locatorExpression)except Exception as e:raise edef switchtodefaultframe(self):try:self.driver.switch_to.default_content()except Exception as e:raise edef usernameobj(self):try:# 从定位表达式配置文件中读取定位用户名如输入框的定位方式和表达式locateType, locatorExpression = self.loginOptions["loginPage.username".lower()].split(">")# 获取登录页面的用户名输入框页面对象,返回给调用者elementobj = get_element(self.driver, locateType, locatorExpression)return elementobjexcept Exception as e:raise edef passwordobj(self):try:# 从定位表达式配置文件中读取定位密码输入框的定位方式和表达式locateType, locatorExpression = self.loginOptions["loginPage.password".lower()].split(">")# 获取登录页面的密码输入框页面对象,并返回给调用者elementoj = get_element(self.driver, locateType, locatorExpression)return elementojexcept Exception as e:raise edef loginbutton(self):try:# 从定位表达式配置文件中读取定位登录按钮的定位方式和表达式locateType, locatorExpression = self.loginOptions["loginPage.loginbutton".lower()].split(">")# 获取登录页面的登录按钮页面对象,并返回给调用者elementobj = get_element(self.driver, locateType, locatorExpression)return elementobjexcept Exception as e:raise eif __name__ == '__main__':from selenium import webdriverimport timedriver = webdriver.Chrome(executable_path="F:\\automation\\webdriver\\chromedriver.exe")driver.get("http://mail.163.com")time.sleep(5)login = LoginPage(driver)login.switchtoframe()login.usernameobj().send_keys("xxxx")login.passwordobj().send_keys("xxxx")login.loginbutton().click()time.sleep(10)login.switchtodefaultframe()assert u"未读邮件" in driver.page_sourcedriver.quit()

简洁的测试脚本

# -*- coding: utf-8 -*-
# @Time: 2019/3/1 13:40
# @Author : Yang DaWei
# @Project : LeadsCloudAutomation
# @FileName: test_login_page.py
import time
import unittest
from selenium import webdriver
from PageObject.Login_Page import Login_Pageclass Test_Login_Page(unittest.TestCase):driver = None@classmethoddef setUpClass(cls):""" set Up method """cls.driver = webdriver.Chrome()cls.test_page = ''time.sleep(3)def tearDown(self):""" tear Down method """def setUp(self):"""setUP pass"""@classmethoddef tearDownClass(cls):""" tear Down method """cls.driver.close()def test_login(self):self.driver.get("http://admin.xxxxxxx.com/Front-Vue/#/")Login_Page(self.driver).input_user_name().send_keys("yangdawei_10171")Login_Page(self.driver).input_user_password().send_keys("111111")Login_Page(self.driver).login_button().click()try:self.assertTrue("仪表盘", self.driver.page_source)print("断言成功")except Exception as e:raise etime.sleep(10)

关键字驱动

在关键字驱动测试框架中,除了PO模式以及一些常规Action的封装外,一个很重要的内容就是读写EXCEL,在团队中如何让不会写代码的人也可以进行自动化测试? 我们可以将自动化测试用例按一定的规格写到EXCEL中去(如下图所示)
在这里插入图片描述
然后通过代码实现对具备这种规格的EXCEL进行解析,让你的代码获取EXCEL中的步骤,关键字,页面元素定位,操作方式,最后在写入执行结果,附上异常截图即可;团队中不会写代码的人居多,改改Excel执行也可以实现自动化测试

此处在初始化类的时候定义了两个颜色放进字典中,之后会当做参数传给写EXCEL的函数,当测试用例执行通过 用绿色字体标注pass,当执行失败的时候用红色字体标注failed

解析关键字

具体实现代码如下

# 用于实现读取Excel数据文件代码封装
# encoding = utf-8
"""
__project__ = 'KeyDri'
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
import openpyxl
from openpyxl.styles import Border, Side, Font
import timeclass ParseExcel(object):def __init__(self):self.workBook = Noneself.excelFile = Noneself.font = Font(color=None)self.RGBDict = {'red': 'FFFF3030', 'green': 'FF008B00'}def loadWorkBook(self, excelPathAndName):# 将Excel加载到内存,并获取其workbook对象try:self.workBook = openpyxl.load_workbook(excelPathAndName)except Exception as e:raise eself.excelFile = excelPathAndNamereturn self.workBookdef getSheetByName(self, sheetName):# 根据sheet名获取该sheet对象try:# sheet = self.workBook.get_sheet_by_name(sheetName)sheet = self.workBook[sheetName]return sheetexcept Exception as e:raise edef getSheetByIndex(self, sheetIndex):# 根据sheet的索引号获取该sheet对象try:# sheetname = self.workBook.get_sheet_names()[sheetIndex]sheetname = self.workBook.sheetnames[sheetIndex]except Exception as e:raise e# sheet = self.workBook.get_sheet_by_name(sheetname)sheet = self.workBook[sheetname]return sheetdef getRowsNumber(self, sheet):# 获取sheet中有数据区域的结束行号return sheet.max_rowdef getColsNumber(self, sheet):# 获取sheet中有数据区域的结束列号return sheet.max_columndef getStartRowNumber(self, sheet):# 获取sheet中有数据区域的开始的行号return sheet.min_rowdef getStartColNumber(self, sheet):# 获取sheet中有数据区域的开始的列号return sheet.min_columndef getRow(self, sheet, rowNo):# 获取sheet中某一行,返回的是这一行所有数据内容组成的tuple# 下标从1开始,sheet.rows[1]表示第一行try:# return sheet.rows[rowNo - 1] 因为sheet.rows是生成器类型,不能使用索引# 转换成list之后再使用索引,list(sheet.rows)[2]这样就获取到第二行的tuple对象。return list(sheet.rows)[rowNo - 1]except Exception as e:raise edef getCol(self, sheet, colNo):# 获取sheet中某一列,返回的是这一列所有数据内容组成的tuple# 下标从1开始,sheet.columns[1]表示第一列try:return list(sheet.columns)[colNo - 1]except Exception as e:raise edef getCellOfValue(self, sheet, coordinate = None, rowNo = None, colNo = None):# 根据单元格所在的位置索引获取该单元格中的值,下标从1开始# sheet.cell(row = 1, column = 1).value,表示excel中的第一行第一列的值if coordinate is not None:try:return sheet.cell(coordinate=coordinate).valueexcept Exception as e:raise eelif coordinate is None and rowNo is not None and colNo is not None:try:return sheet.cell(row=rowNo, column=colNo).valueexcept Exception as e:raise eelse:raise Exception("Insufficient Coordinates of cell!")def getCellOfObject(self, sheet, coordinate = None, rowNo = None, colNo = None):# 获取某个单元格对象,可以根据单元格所在的位置的数字索引,也可以直接根据Excel中单元格的编码及坐标# 如getCellOfObject(sheet, coordinate='A1) or getCellOfObject(sheet, rowNo = 1, colNo = 2)if coordinate is not None:try:return sheet.cell(coordinate=coordinate)except Exception as e:raise eelif coordinate is None and rowNo is not None and colNo is not None:try:return sheet.cell(row=rowNo, column=colNo)except Exception as e:raise eelse:raise Exception("Insufficient Coordinates of cell!")def writeCell(self, sheet, content, coordinate = None, rowNo = None, colNo = None, style=None):# 根据单元格在Excel中的编码坐标或者数字索引坐标向单元格中写入数据,下标从1开始# 参数style表示字体的颜色的名字,如red,greenif coordinate is not None:try:sheet.cell(coordinate=coordinate).value = contentif style is not None:sheet.cell(coordinate=coordinate).font = Font(color=self.RGBDict[style])self.workBook.save(self.excelFile)except Exception as e:raise eelif coordinate is None and rowNo is not None and colNo is not None:try:sheet.cell(row=rowNo, column=colNo).value = contentif style is not None:sheet.cell(row=rowNo, column=colNo).font = Font(color=self.RGBDict[style])self.workBook.save(self.excelFile)except Exception as e:raise eelse:raise Exception("Insufficient Coordinates of cell!")def writeCellCurrentTime(self, sheet, coordinate = None, rowNo = None, colNo = None):# 写入当前时间,下标从1开始now = int(time.time())  # 显示为时间戳timeArray = time.localtime(now)currentTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)if coordinate is not None:try:sheet.cell(coordinate=coordinate).value = currentTimeself.workBook.save(self.excelFile)except Exception as e:raise eelif coordinate is None and rowNo is not None and colNo is not None:try:sheet.cell(row=rowNo, column=colNo).value = currentTimeself.workBook.save(self.excelFile)except Exception as e:raise eif __name__ == "__main__":from Configurations.VarConfig import dataFilePath163pe = ParseExcel()pe.loadWorkBook(dataFilePath163)print("通过名称获取sheet对象名字:")pe.getSheetByName(u"联系人")print("通过Index序号获取sheet对象的名字")pe.getSheetByIndex(0)sheet = pe.getSheetByIndex(0)print(type(sheet))print(pe.getRowsNumber(sheet))print(pe.getColsNumber(sheet))cols = pe.getCol(sheet, 1)for i in cols:print(i.value)# 获取第一行第一列单元格内容print(pe.getCellOfValue(sheet, rowNo=1, colNo=1))pe.writeCell(sheet, u'中国北京', rowNo=11, colNo=11, style='red')pe.writeCellCurrentTime(sheet, rowNo=10, colNo=11)

解析Excel实际上方法不一


# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
import xlrdclass ExcelUtil():def __init__(self, excelPath, sheetName):self.data = xlrd.open_workbook(excelPath)self.table = self.data.sheet_by_name(sheetName)# 获取第一行作为key值self.keys = self.table.row_values(0)# 获取总行数self.rowNum = self.table.nrows# 获取总列数self.colNum = self.table.ncolsdef dict_data(self):if self.rowNum <= 1:print("总行数小于1")else:r = []j = 1for i in range(self.rowNum -1):s = {}# 从第二行取对应values值values = self.table.row_values(j)for x in range(self.colNum):s[self.keys[x]] = values[x]r.append(s)j += 1return rif __name__ == '__main__':excelPath = u"F:\\seleniumWithPython\\TestData\\163邮箱联系人.xlsx"sheetName = u"163账号"data = ExcelUtil(excelPath, sheetName)print(data.dict_data())# 打开exlce表格,参数是文件路径
# data = xlrd.open_workbook('test.xlsx')# table = data.sheets()[0]           #  通过索引顺序获取
# table = data.sheet_by_index(0)     #  通过索引顺序获取
# table = data.sheet_by_name(u'Sheet1')  # 通过名称获取# nrows = table.nrows  # 获取总行数
# ncols = table.ncols  # 获取总列数# 获取一行或一列的值,参数是第几行
#  print table.row_values(0)  # 获取第一行值
#  print table.col_values(0)  # 获取第一列值

关键字驱动测试代码

# 用于编写具体的测试逻辑代码
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from Util.GetElements import *
from Util.KeyBoardOperations import Keyboardkeys
from Util.ClipboardUtil import Clipboard
from Util.SmartWaitElements import WaitUtil
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from KeyDrivenMode.Actions.PageAction import *
import timefrom KeyDrivenMode.Actions.PageAction import *
from Util.ParseExcelFile import ParseExcel
from Configurations.VarConfig import *
import time
import tracebackexcelObj = ParseExcel()
excelObj.loadWorkBook(dataFilePath126)def writeTestResult(sheetObj, rowNo, colNo, testResult, errorInfo = None, picPath = None):# 测试通过结果信息为绿色,失败为红色colorDict = {"pass": "green", "fail": "red"}# 因为测试用例工作表和用例步骤工作表中都有测试执行时间和测试结果列,定义此字典对象是为了区分具体应该写哪个工作表colDict = {"testCase": [testCase_runTime, testCase_testResult], "caseStep": [testStep_runTime, testStep_testResult]}try:# 在测试步骤工作表中,写入测试时间excelObj.writeCellCurrentTime(sheetObj, rowNo=rowNo, colNo=colDict[colNo][0])# 在测试步骤工作表中,写入测试结果excelObj.writeCell(sheetObj, content=testResult, rowNo=rowNo, colNo=colDict[colNo][1])if errorInfo and picPath:# 在测试步骤工作表中,写入异常信息excelObj.writeCell(sheetObj, content=errorInfo, rowNo=rowNo, colNo=testStep_errorInfo)# 在测试步骤工作表中,写入异常截图路径excelObj.writeCell(sheetObj, content=picPath, rowNo=rowNo, colNo=testStep_errorPic)else:# 在测试步骤工作表中,清空异常信息单元格excelObj.writeCell(sheetObj, content="", rowNo=rowNo, colNo=testStep_errorInfo)# 在测试步骤工作表中,清空异常截图路径单元格excelObj.writeCell(sheetObj, content="", rowNo=rowNo, colNo=testStep_errorPic)except Exception as e:print(u"写excel出错", traceback.print_exc())def TestSendMailWithAttachment():try:# 根据Excel文件中的sheet名获取sheet对象caseSheet = excelObj.getSheetByName(u"测试用例")# 获取测试用例sheet中是否执行列对象isExeuteColumn = excelObj.getCol(caseSheet, testCase_isExecute)# 记录执行成功的测试用例个数successfulCase = 0# 记录需要执行的用力个数requiredCase = 0for idx, i in enumerate(isExeuteColumn[1:]):# 因为用例sheet中的第一行为标题行,无需执行# print(i.value)# 循环遍历“测试用例”表中的测试用例,执行被设置为执行的用例if i.value.lower() == "y":requiredCase += 1# 获取“测试用例”表中第idx+2行的数据caseRow = excelObj.getRow(caseSheet, idx +2)# 获取idx+2行的“步骤sheet”单元格内容caseStepSheetName = caseRow[testCase_testStepSheetName - 1].value# print(caseStepSheetName)# 根据用例步骤名获取步骤sheet对象stepSheet = excelObj.getSheetByName(caseStepSheetName)# 获取步骤sheet中步骤数stepNum = excelObj.getRowsNumber(stepSheet)# print(stepNum)# 记录测试用例i的步骤成功数successfulSteps = 0print(u"开始执行用例%s" % list(caseRow)[testCase_testCaseName - 1].value)for step in range(2, stepNum + 1):# 因为步骤sheet中的第一行为标题行,无需执行# 获取步骤sheet中的第step行对象stepRow = excelObj.getRow(stepSheet, step)# 获取关键字作为调用的函数名keyWord = stepRow[testStep_keyWords - 1].value# 获取操作元素定位方式作为调用的函数的参数locationType = stepRow[testStep_locationType - 1].value# 获取操作元素定位表达式作为调用函数的参数locatorExpression = stepRow[testStep_locatorExpression - 1].value# 获取操作值作为调用函数的参数operateValue = stepRow[testStep_operateValue - 1].value# 将操作值为数字类型的数据转换成字符串类型,方便字符串拼接if isinstance(operateValue, int):operateValue = str(operateValue)print(keyWord, locationType, locatorExpression, operateValue)expressionStr = ""# 构造需要执行的python语句# 对应的是PageAction.py文件中的页面动作函数调用的字符串表示if keyWord and operateValue and locationType is None and locatorExpression is None:expressionStr = keyWord.strip() + "(u'" +operateValue+"')"elif keyWord and operateValue is None and locationType is None and locatorExpression is None:expressionStr = keyWord.strip() + str()elif keyWord and locationType and operateValue and locatorExpression is None:expressionStr = keyWord.strip() + "('" + locationType.strip() + "', u'" + operateValue + "')"elif keyWord and locationType and locatorExpression and operateValue:expressionStr = keyWord.strip() + "('" + locationType.strip() + "', '" + locatorExpression.replace("'", '"').strip() + "', u'" + operateValue + "')"elif keyWord and locationType and locatorExpression and operateValue is None:expressionStr = keyWord.strip() + "('" + locationType.strip() + "', '" + locatorExpression.replace("'", '"').strip() + "')"print(expressionStr)try:# 通过eval函数,将拼接的页面动作函数调用的字符串表示# 当成有效的Python表达式执行,从而执行测试步骤的sheet中# 关键字在PageAction.py文件中对应的映射方法# 来完成对页面元素的操作eval(expressionStr)# 在测试执行时间列写入执行时间excelObj.writeCellCurrentTime(stepSheet, rowNo=step, colNo=testStep_runTime)except Exception as e:# 截取异常屏幕图片capturePic = capture_screen()# 获取详细的异常堆栈信息errorInfo = traceback.format_exc()# 在测试步骤sheet中写入失败信息writeTestResult(stepSheet, step, "caseStep", "faild", errorInfo, capturePic)print(u"步骤 %s 执行失败!" % list(stepRow)[testStep_testStepDescribe - 1].value)else:# 在测试步骤sheet中写入成功信息writeTestResult(stepSheet, step, "caseStep", "pass")# 每成功一步successfulSteps变量自增1successfulSteps += 1print(u"步骤 %s 执行成功!" % list(stepRow)[testStep_testStepDescribe - 1].value)if successfulSteps == stepNum - 1:# 当测试用例步骤sheet中所有的步骤都执行成功# 才认为此测试用例执行通过,然后将成功信息写入# 测试用例工作表中,否则写入失败信息writeTestResult(caseSheet, idx + 2, "testCase", "pass")successfulCase += 1else:writeTestResult(caseSheet, idx+2, "testCase", "faild")print(u"共%d条用例,%d条需要被执行,本次直行通过%d条." % (len(isExeuteColumn)-1, requiredCase, successfulCase))except Exception as e:# 打印详细的异常堆栈信息print(traceback.print_exc())if __name__ == "__main__":TestSendMailWithAttachment()"""不借用PageAction.py# 创建Chrome浏览器实例driverchrome = webdriver.Chrome(executable_path=r"F:\automation\webdriver\chromedriver.exe")driverchrome.maximize_window()driverchrome.get("http://mail.126.com")time.sleep(5)assert u"126网易免费邮--你的专业电子邮局" in driverchrome.titleprint("access 126 successfully")wait = WaitUtil(driverchrome)wait.frame_to_be_available_and_switch_to_it("id", "x-URS-iframe")username = get_element(driverchrome, "xpath", "//input[@name='email']")username.send_keys("davieyang008")password = get_element(driverchrome, "xpath", "//input[@name='password']")password.send_keys("Ethan005x")password.send_keys(Keys.ENTER)time.sleep(5)assert u"网易邮箱" in driverchrome.titleprint("login successfully")element = wait.visibility_element_located("xpath", "//li[@id='_mail_component_70_70']")element.click()receiver = get_element(driverchrome, "xpath", "//div[contains(@id, '_mail_emailinput')]/input")receiver.send_keys("15901281916@163.com")subject = get_element(driverchrome, "xpath", "//div[@aria-label = '邮件主题输入框,请输入邮件主题']/input")subject.send_keys("new email")# 设置剪切板内容Clipboard.setText(u"F:\\a.txt")# 获取剪切板内容Clipboard.getText()attachment = get_element(driverchrome, "xpath", "//div[contains(@title, '点击添加附件')]")# 单击上传附件链接attachment.click()time.sleep(3)# 在上传附件windows弹窗中黏贴剪切板中的内容Keyboardkeys.twoKey('ctrl', 'v')# 模拟回车以便加载要上传的附件time.sleep(3)Keyboardkeys.oneKey('enter')# 切换进邮件正文的framewait.frame_to_be_available_and_switch_to_it("xpath", "//iframe[@tabindex=1]")body = get_element(driverchrome, "xpath", "/html/body")# 输入邮件正文body.send_keys("abcabc")# 切出邮件正文的framedriverchrome.switch_to.default_content()get_element(driverchrome, "xpath", "//header//span[text()='发送']").click()time.sleep(3)assert u"发送成功" in driverchrome.page_sourceprint(u"邮件发送成功")driverchrome.quit():return:""""""借用PageAction.pyopen_browser("chrome")maximize_browser()visit_url("http://mail.126.com")sleep(5)assert_string_in_pagesource(u"126网易免费邮--你的专业电子邮局")waitFrameToBeAvailableAndSwitchToIt("id", "x-URS-iframe")input_string("xpath", "//input[@name='email']", "davieyang008")input_string("xpath", "//input[@name='password']", "Ethan005x")click("id", "dologin")sleep(5)assert_title(u"网易邮箱")waitVisibilityOfElementLocated("xpath", "//li[@id='_mail_component_70_70']")click("xpath", "//li[@id='_mail_component_70_70']")input_string("xpath", "//div[contains(@id, '_mail_emailinput')]/input", "15901281916@163.com")input_string("xpath", "//div[@aria-label = '邮件主题输入框,请输入邮件主题']/input", "new email")click("xpath", "//div[contains(@title, '点击添加附件')]")sleep(3)paste_string(u"F:\\a.txt")press_enter_key()waitFrameToBeAvailableAndSwitchToIt("xpath", "//iframe[@tabindex=1]")input_string("xpath", "/html/body", "abcdef")switch_to_default_content()click("xpath", "//header//span[text()='发送']")time.sleep(3)assert_string_in_pagesource(u"发送成功")close_browser()"""

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/43184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLM 入门与实践(四) Yi 部署与分析

本文截取自20万字的《PyTorch实用教程》&#xff08;第二版&#xff09;&#xff0c;敬请关注&#xff1a;《Pytorch实用教程》&#xff08;第二版&#xff09;无论是零基础入门&#xff0c;还是CV、NLP、LLM项目应用&#xff0c;或是进阶工程化部署落地&#xff0c;在这里都有…

python对象

类 我们目前所学习的对象都是Python内置的对象但是内置对象并不能满足所有的需求&#xff0c;所以我们在开发中经常需要自定义一些对象类&#xff0c;简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象类就是对象的图纸&#xff01;我们也称对象是类的实例&#…

JAVA--IO流

一、IO流什么&#xff1f; I/O是Input/output的缩写&#xff0c;用于处理设备之间的数据传输。如读/写文件、网络通讯。 java程序中&#xff0c;对于数据的输入、输出是以流&#xff08;Stream)的方式进行。 Java.io包下提供了各种流类和接口&#xff0c;用以获取不同种类的数…

ELfK logstash filter模块常用的插件 和ELFK部署

ELK之filter模块常用插件 logstash filter模块常用的插件&#xff1a; filter&#xff1a;表示数据处理层&#xff0c;包括对数据进行格式化处理、数据类型转换、数据过滤等&#xff0c;支持正则表达式 grok 对若干个大文本字段进行再分割成一些小字段 (?<字段名…

【算法篇】KMP算法,一种高效的字符串匹配算法

我们今天了解一个字符串匹配算法-KMP算法&#xff0c;内容难度相对来说较高&#xff0c;建议先收藏再细品&#xff01;&#xff01;&#xff01; KMP算法的基本概念 KMP算法是一种高效的字符串匹配算法&#xff0c;由D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt提出的&#…

LLMs之gptpdf:gptpdf的简介、安装和使用方法、案例应用之详细攻略

LLMs之gptpdf&#xff1a;gptpdf的简介、安装和使用方法、案例应用之详细攻略 目录 gptpdf的简介 1、处理流程 第一步&#xff0c;使用 PyMuPDF 库&#xff0c;对 PDF 进行解析出所有非文本区域&#xff0c;并做好标记&#xff0c;比如: 第二步&#xff0c;使用视觉大模型&…

MySQL安装时initializing database失败

问题页面&#xff1a; 解决方法&#xff1a; 1.勾选红框中的选项&#xff1a; 2.将下图红框中全部改为英文&#xff1a; 然后一路next就可以了。

cs231n作业1——KNN

参考文章&#xff1a;assignment1——KNN KNN 测试时分别计算测试样本和训练集中的每个样本的距离&#xff0c;然后选取距离最近的k个样本的标签信息来进行分类。 方法1&#xff1a;Two Loops for i in range(num_test):for j in range(num_train):dist X[i, :] - self.X…

vue3使用方式汇总

1、引入iconfont阿里图库图标&#xff1a; 1.1 进入阿里图标网站&#xff1a; iconfont阿里&#xff1a;https://www.iconfont.cn/ 1.2 添加图标&#xff1a; 1.3 下载代码&#xff1a; 1.4 在vue3中配置代码&#xff1a; 将其代码复制到src/assets/fonts/目录下&#xff1…

mp4视频太大怎么压缩不影响画质,mp4文件太大怎么变小且清晰度高

在数字化时代&#xff0c;我们常常面临视频文件过大的问题。尤其是mp4格式的视频&#xff0c;文件大小往往令人望而却步。那么&#xff0c;如何在不影响画质的前提下&#xff0c;有效地压缩mp4视频呢&#xff1f;本文将为您揭秘几种简单实用的压缩技巧。 在分享和存储视频时&am…

Open3D 计算点云的欧式距离

目录 一、概述 1.1欧式距离定义 1.2作用和用途 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2处理后点云 一、概述 在Open3D中&#xff0c;compute_point_cloud_distance函数用于计算两个点云之间的距离。具体来说&#xff0c;它计算的是源点云…

【计算机网络仿真】b站湖科大教书匠思科Packet Tracer——实验16 路由信息协议RIP

一、实验目的 1.验证RIP协议的作用&#xff1b; 二、实验要求 1.使用Cisco Packet Tracer仿真平台&#xff1b; 2.观看B站湖科大教书匠仿真实验视频&#xff0c;完成对应实验。 三、实验内容 1.构建网络拓扑&#xff1b; 2.验证RIP协议。 四、实验步骤 1.构建网络拓扑 …

【图像处理】Krita 一款开源免费专业图像处理软件分享

软件介绍 Krita 是一款专业级的图像处理软件&#xff0c;适合数字绘画和创作。它不仅支持栅格图像的细致编辑&#xff0c;还提供了强大的矢量图形工具&#xff0c;使得用户可以在同一个平台上完成多种类型的创作工作。同时具备一定的矢量图形编辑功能。Krita 的首要用途是绘画…

黑马点评商户缓存查询作业——Redis中查询商户类型

记录下自己在gpt帮助下完成的第一个需求~~~ 1. ShopTypeController 2. IShopTypeService 3. ShopTypeServiceImpl&#xff08;模仿ShopServiceImpl来写的&#xff09; 一共分为“1.redis中查询缓存”→“2.判断缓存是否存在&#xff0c;存在直接返回”→“3.缓存不存在则去查数…

2-28 基于matlab提取出频域和时域信号的29个特征

基于matlab提取出频域和时域信号的29个特征&#xff0c;主运行文件feature_extraction&#xff0c;fre_statistical_compute和time_statistical_compute分别提取频域和时域的特征&#xff0c;生成的29个特征保存在生成的feature矩阵中。程序已调通&#xff0c;可直接运行。 2-2…

C语言 printf 函数多种输出格式以及占位输出

一、输出格式 在C语言中&#xff0c;printf 函数提供了多种输出格式&#xff0c;用于控制不同类型数据的输出方式。 1.整数输出格式 %d&#xff1a;以十进制形式输出整数。 %o&#xff1a;以八进制形式输出整数&#xff08;无前导0&#xff09;。 %x 或 %X&#xff1a;以十六进…

EAI四个层次服务-系统架构师(二十六)

1、&#xff08;重点&#xff09;系统应用集成提供了4个不同层次服务&#xff0c;最上层服务是&#xff08;&#xff09;服务。 解析: EAI&#xff08;Enterprise Application Integration&#xff09;系统应用集成&#xff0c;相关概念。 实施EAI必须保证&#xff1a;应用程…

星间链路与星地链路

目录 一、星间链路 1.1 层内星间链路&#xff08;Intra-layer ISLs&#xff09; 1.2 层间星间链路&#xff08;Inter-layer ISLs&#xff09; 1.3 实现方式 1.3.1 微波链路 1.3.2 激光链路 二、星地链路 2.1 星地链路的关键特性 2.1.2 Ka信关站 2.1.2 Q/V信关站 2.1…

JavaScript中,正则表达式所涉及的api,解析、实例和总结

JS中正则的api包括以下&#xff1a; String#searchString#splitString#matchString#replaceRegExp#testRegExp#exec 1. String#search 查找输入串中第一个匹配正则的index&#xff0c;如果没有匹配的则返回-1。g修饰符对结果无影响 var string "abbbcbc"; var r…

谷粒商城学习笔记-使用renren-fast-vue框架时安装依赖包遇到的问题及解决策略

文章目录 1&#xff0c;npm error Class extends value undefined is not a constuctor or null2&#xff0c;npm warn cli npm v10.8.1 does not support Node.js v16.20.2.3&#xff0c;npm error code CERT_HAS_EXPIRED学习心得 这篇文章记录下使用renren-fast-vue&#xff…