在关键字驱动测试框架中,除了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 time
class ParseExcel(object):
def __init__(self):
self.workBook = None
self.excelFile = None
self.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 e
self.excelFile = excelPathAndName
return self.workBook
def getSheetByName(self, sheetName):
# 根据sheet名获取该sheet对象
try:
# sheet = self.workBook.get_sheet_by_name(sheetName)
sheet = self.workBook[sheetName]
return sheet
except Exception as e:
raise e
def 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 sheet
def getRowsNumber(self, sheet):
# 获取sheet中有数据区域的结束行号
return sheet.max_row
def getColsNumber(self, sheet):
# 获取sheet中有数据区域的结束列号
return sheet.max_column
def getStartRowNumber(self, sheet):
# 获取sheet中有数据区域的开始的行号
return sheet.min_row
def getStartColNumber(self, sheet):
# 获取sheet中有数据区域的开始的列号
return sheet.min_column
def 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 e
def getCol(self, sheet, colNo):
# 获取sheet中某一列,返回的是这一列所有数据内容组成的tuple
# 下标从1开始,sheet.columns[1]表示第一列
try:
return list(sheet.columns)[colNo - 1]
except Exception as e:
raise e
def 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).value
except Exception as e:
raise e
elif coordinate is None and rowNo is not None and colNo is not None:
try:
return sheet.cell(row=rowNo, column=colNo).value
except Exception as e:
raise e
else:
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 e
elif 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 e
else:
raise Exception("Insufficient Coordinates of cell!")
def writeCell(self, sheet, content, coordinate = None, rowNo = None, colNo = None, style=None):
# 根据单元格在Excel中的编码坐标或者数字索引坐标向单元格中写入数据,下标从1开始
# 参数style表示字体的颜色的名字,如red,green
if coordinate is not None:
try:
sheet.cell(coordinate=coordinate).value = content
if style is not None:
sheet.cell(coordinate=coordinate).font = Font(color=self.RGBDict[style])
self.workBook.save(self.excelFile)
except Exception as e:
raise e
elif coordinate is None and rowNo is not None and colNo is not None:
try:
sheet.cell(row=rowNo, column=colNo).value = content
if 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 e
else:
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 = currentTime
self.workBook.save(self.excelFile)
except Exception as e:
raise e
elif coordinate is None and rowNo is not None and colNo is not None:
try:
sheet.cell(row=rowNo, column=colNo).value = currentTime
self.workBook.save(self.excelFile)
except Exception as e:
raise e
if __name__ == "__main__":
from Configurations.VarConfig import dataFilePath163
pe = 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)
# 用于编写具体的测试逻辑代码
# 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 time
from KeyDrivenMode.Actions.PageAction import *
from Util.ParseExcelFile import ParseExcel
from Configurations.VarConfig import *
import time
import traceback
excelObj = 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 = 0
for 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 = 0
print(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变量自增1
successfulSteps += 1
print(u"步骤 %s 执行成功!" % list(stepRow)[testStep_testStepDescribe - 1].value)
if successfulSteps == stepNum - 1:
# 当测试用例步骤sheet中所有的步骤都执行成功
# 才认为此测试用例执行通过,然后将成功信息写入
# 测试用例工作表中,否则写入失败信息
writeTestResult(caseSheet, idx + 2, "testCase", "pass")
successfulCase += 1
else:
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.title
print("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.title
print("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("[email protected]")
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')
# 切换进邮件正文的frame
wait.frame_to_be_available_and_switch_to_it("xpath", "//iframe[@tabindex=1]")
body = get_element(driverchrome, "xpath", "/html/body")
# 输入邮件正文
body.send_keys("abcabc")
# 切出邮件正文的frame
driverchrome.switch_to.default_content()
get_element(driverchrome, "xpath", "//header//span[text()='发送']").click()
time.sleep(3)
assert u"发送成功" in driverchrome.page_source
print(u"邮件发送成功")
driverchrome.quit()
:return:
"""
"""
借用PageAction.py
open_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", "[email protected]")
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()
"""