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()"""