搭建关键字驱动自动化测试框架

前言
上篇文章我们已经了解到了数据驱动自动化测试框架是如何构建和驱动测试的!那么这篇文章我们将了解关键字驱动测试又是如何驱动自动化测试完成整个测试过程的。关键字驱动框架是一种功能自动化测试框架,它也被称为表格驱动测试或者基于动作字的测试。关键字驱动的框架的基本工作是将测试用例分成四个不同的部分。首先是测试步骤(Test Step),二是测试步骤中的对象(Test Object),三是测试对象执行的动作(Action),四是测试对象需要的数据(Test Data)。

其实我们做关键字的驱动的思想,就是把编码从测试用例和测试步骤中分离出来,这样对于不会编码的人员更容易理解自动化,从而让手工测试人员也可以编写自动脚本。(这并不意味这不需要自动化测试人员,对于自动化框架的构建,自动化代码的更新,结构调整等都需要一个技术性的人员)对于测试小的项目的团队,可以有两个手工测试人员和一个自动化测试人员。

项目功能
我们今天将要实现的功能是测试126邮箱的登录及登录后发送一封带附件的邮件

测试地址

126网易免费邮-你的专业电子邮局

项目目录
接下来我们来看看我们的项目目录是如何设计的,每个目录的功能是用来做什么的?

框架搭建

接下来我们一步一步来考虑如何搭建整个项目及每个py代码文件如何编写?

框架主要功能模块

1.新建util文件夹,并在此文件夹下新建ObjectMap.py文件,主要实现页面元素查找功能的封装

1 from selenium.webdriver.support.wait import WebDriverWait
 2 
 3 
 4 def getElement(driver, by, locator):
 5     '''
 6     查找单一元素
 7     :param driver:
 8     :param by:
 9     :param locator:
10     :return: 元素对象
11     '''
12     try:
13         element = WebDriverWait(driver, 30).until(lambda x : x.find_element(by, locator))
14     except Exception as e:
15         raise e
16     else:
17         return element
18 
19 def getElements(driver, by, locator):
20     '''
21     获取一组元素
22     :param driver:
23     :param by:
24     :param locator:
25     :return: 一组元素对象
26     '''
27     try:
28         elements = WebDriverWait(driver, 30).until(lambda x : x.find_element(by, locator))
29     except Exception as e:
30         raise e
31     else:
32         return elements
33 
34 
35 if __name__=="__main__":
36     from selenium import webdriver
37     import time
38 
39     driver = webdriver.Firefox()
40     driver.get('https://mail.126.com')
41     time.sleep(5)
42     driver.switch_to.frame(getElement(driver, 'xpath', "//div[@id='loginDiv']/iframe"))
43     username = getElement(driver, 'xpath', "//input[@name='email']")
44     username.send_keys('linuxxiaochao')
45     driver.switch_to.default_content()
46     driver.quit()

 2.util文件夹下新建WaitUntil.py文件,主要实现显示等待元素功能的封装

 1 from selenium.webdriver.common.by import By
 2 from selenium.webdriver.support.wait import WebDriverWait
 3 from selenium.webdriver.support import expected_conditions as EC
 4 
 5 class WaitUnit(object):
 6     def __init__(self, driver):
 7         self.byDic = {
 8             'id': By.ID,
 9             'name': By.NAME,
10             'class_name': By.CLASS_NAME,
11             'xpath': By.XPATH,
12             'link_text': By.LINK_TEXT
13         }
14         self.driver = driver
15         self.wait = WebDriverWait(self.driver, 50)
16 
17     def presenceOfElementLocated(self, by, locator):
18         '''
19         显示等待某个元素出现在dom中,不一定可见,存在返回元素对象
20         :param by:
21         :param locator:
22         :return:
23         '''
24         try:
25             if by.lower() in self.byDic:
26                 self.wait.until(EC.presence_of_element_located((self.byDic[by.lower()], locator)))
27             else:
28                 raise TypeError('未找到定位方式,请确保定位方式正确')
29         except Exception as e:
30             raise e
31 
32     def frameToBeAvailableAndSwtichToIt(self, by, locator):
33         '''
34         检查frame是否存在,存在就切换到frame中
35         :param by:
36         :param locator:
37         :return:
38         '''
39         try:
40             if by.lower() in self.byDic:
41                 self.wait.until(EC.frame_to_be_available_and_switch_to_it((self.byDic[by.lower()], locator)))
42             else:
43                 raise TypeError('未找到定位方式,请确保定位方式正确')
44         except Exception as e:
45             raise e
46     def visibiltyOfElementLocated(self, by, locator):
47         '''
48         显示等待页面元素出现在dom中, 并且可见, 存在则返回该元素对象
49         :param by:
50         :param locator:
51         :return:
52         '''
53         try:
54             if by.lower() in self.byDic:
55                 self.wait.until(EC.visibility_of_element_located((self.byDic[by.lower()], locator)))
56             else:
57                 raise TypeError('未找到定位方式,请确保定位方式正确')
58         except Exception as e:
59             raise e
60 
61 if __name__=='__main__':
62     from selenium import webdriver
63     from util.ObjectMap import *
64     driver = webdriver.Firefox()
65     driver.get('https://mail.126.com')
66 
67     wait = WaitUnit(driver)
68     wait.frameToBeAvailableAndSwtichToIt('xpath', "//div[@id='loginDiv']/iframe")
69     wait.visibiltyOfElementLocated('xpath', "//input[@name='email']")
70     uname = getElement(driver, 'xpath', "//input[@name='email']")
71     uname.send_keys('python')
72     driver.quit()

3.新建ClipboardUtil.py文件,用来实现剪切版的操作(我们发送邮件时,需要添加附件,通过这个功能来实现上传附件)

 
 1 import win32clipboard as w
 2 import win32con
 3 
 4 class Clipboard(object):
 5 
 6     @staticmethod
 7     def getText():
 8         '''
 9         获取剪切板的内容
10         :return:
11         '''
12 
13         try:
14             # 打开剪切板
15             w.OpenClipboard()
16             # 读取数据
17             value = w.GetClipboardData(win32con.CF_TEXT)
18             # 关闭剪切板
19             w.CloseClipboard()
20         except Exception as e:
21             raise e
22         else:
23             return value
24 
25     @staticmethod
26     def setText(value):
27         '''
28         设置剪切板内容
29         :return:
30         '''
31         try:
32             w.OpenClipboard()# 打开剪切板
33             w.EmptyClipboard()# 清空剪切板
34             w.SetClipboardData(win32con.CF_UNICODETEXT, value) # 设置内容
35             w.CloseClipboard() # 关闭
36         except Exception as e:
37             raise e
38 
39 if __name__=='__main__':
40     from selenium import webdriver
41 
42     value = 'python'
43     driver = webdriver.Firefox()
44     driver.get('http://www.baidu.com')
45     query = driver.find_element_by_id('kw')
46     Clipboard.setText(value)
47     clValue = Clipboard.getText()
48     query.send_keys(clValue.decode('utf-8'))

4.新建KeyBoardUtil.py文件,主要实现模拟键盘的操作(配合上面剪切板的功能实现,粘贴附件的路径,回车等)

 1 import win32api
 2 import win32con
 3 
 4 class KeyBoardKeys(object):
 5     '''
 6     模拟键盘
 7     '''
 8     # 键盘编码
 9     vk_code ={
10         'enter':0x0D,
11         'tab' : 0x09,
12         'ctrl':0x11,
13         'v':0x56
14     }
15     @staticmethod
16     def keyDown(keyName):
17         '''
18         模拟按下键
19         :param keyName:
20         :return:
21         '''
22         try:
23             win32api.keybd_event(KeyBoardKeys.vk_code[keyName],0,0,0)
24         except Exception as e:
25             raise e
26     @staticmethod
27     def keyUp(keyName):
28         '''
29         释放键
30         :param keyName:
31         :return:
32         '''
33         try:
34             win32api.keybd_event(KeyBoardKeys.vk_code[keyName],0,win32con.KEYEVENTF_KEYUP,0)
35         except Exception as e:
36             raise e
37     @staticmethod
38     def oneKey(key):
39         '''
40         模拟当个按键
41         :param key:
42         :return:
43         '''
44         try:
45             KeyBoardKeys.keyDown(key)
46             KeyBoardKeys.keyUp(key)
47         except Exception as e:
48             raise e
49 
50     @staticmethod
51     def twoKeys(key1, key2):
52         '''
53         模拟组合按键
54         :param key1:
55         :param key2:
56         :return:
57         '''
58         try:
59             KeyBoardKeys.keyDown(key1)
60             KeyBoardKeys.keyDown(key2)
61             KeyBoardKeys.keyUp(key1)
62             KeyBoardKeys.keyUp(key2)
63         except Exception as e:
64             raise e
65 
66 if __name__=='__main__':
67     from selenium import webdriver
68 
69     driver = webdriver.Firefox()
70     driver.get('http://www.baidu.com')
71     driver.find_element_by_id('kw').send_keys('python')
72     KeyBoardKeys.oneKey('enter')

5.新建DirAndTime.py文件,主要实现获取当前时间,生成特殊路径,这里主要用来生成屏幕截图保存的路径及图片名称

 
 1 from datetime import datetime, date
 2 from config.VarConfig import *
 3 
 4 class DirAndTime(object):
 5     @staticmethod
 6     def getCurrentDate():
 7         '''
 8         获取当前日期
 9         :return:
10         '''
11         try:
12             currentDate = date.today()
13         except Exception as e:
14             raise e
15         else:
16             return str(currentDate)
17     @staticmethod
18     def getCurrentTime():
19         '''
20         获取当前时间
21         :return:
22         '''
23         try:
24             Time = datetime.now()
25             currentTime = Time.strftime('%H_%M_%S')
26         except Exception as e:
27             raise e
28         else:
29             return currentTime
30     @staticmethod
31     def CreatePicturePath():
32         '''
33         创建图片存放路径路径
34         :return:
35         '''
36         try:
37 
38             picturePath = os.path.join(exceptionPath , DirAndTime.getCurrentDate())
39             if not os.path.exists(picturePath):
40                 os.makedirs(picturePath) # 生成多级目录
41         except Exception as e:
42             raise e
43         else:
44             return picturePath
45 
46 if __name__=='__main__':
47     print(DirAndTime.getCurrentDate())
48     print(DirAndTime.getCurrentTime())
49     print(DirAndTime.CreatePicturePath())

6.新建ParseExcel.py用来解析excel文件

 
 1 from openpyxl import load_workbook
 2 from config.VarConfig import *
 3 from datetime import datetime, date
 4 
 5 class ParseExcel(object):
 6     '''
 7     解析excel文件的封装
 8     '''
 9     def __init__(self):
10         # 加载excel文件到内存
11         self.wb = load_workbook(excelPath)
12 
13     def getRowValue(self, sheetName, rawNo):
14         '''
15         获取某一行的数据
16         :param sheetName:
17         :param rawNo:
18         :return: 列表
19         '''
20         sh = self.wb[sheetName]
21         rowValueList = []
22         for y in range(2, sh.max_column+1):
23             value = sh.cell(rawNo,y).value
24             rowValueList.append(value)
25         return rowValueList
26     def getColumnValue(self, sheetName, colNo):
27         '''
28         获取某一列的数据
29         :param sheetName:
30         :param colNo:
31         :return: 列表
32         '''
33         sh = self.wb[sheetName]
34         colValueList = []
35         for x in range(2, sh.max_row +1):
36             value = sh.cell(x, colNo).value
37             colValueList.append(value)
38         return colValueList
39 
40     def getCellOfValue(self, sheetName, rowNo, colNo):
41         '''
42         获取某一个单元格的数据
43         :param sheetName:
44         :param rowNo:
45         :param colNo:
46         :return: 字符串
47         '''
48         sh = self.wb[sheetName]
49         value = sh.cell(rowNo, colNo).value
50         return value
51     def writeCell(self, sheetName, rowNo, colNo, value):
52         '''
53         向某个单元格写入数据
54         :param rowNo: 行号
55         :param colNo: 列号
56         :param value:
57         :return: 无
58         '''
59         sh = self.wb[sheetName]
60         sh.cell(rowNo, colNo).value = value
61         self.wb.save(excelPath)
62     def writeCurrentTime(self, sheetName, rowNo, colNo):
63         '''
64         向某个单元格写入当前时间
65         :return:
66         '''
67         sh = self.wb[sheetName]
68         Time = datetime.now()
69         currentTime = Time.strftime('%Y:%m:%d %H:%M:%S')
70         sh.cell(rowNo, colNo).value = currentTime
71         self.wb.save(excelPath)
72 
73     def writeTestResult(self, sheetName, rowNo, result, errorInfo = None, errorPic = None):
74         ParseExcel().writeCurrentTime(sheetName, rowNo, testStep_testRunTime)
75         ParseExcel().writeCell(sheetName, rowNo, testStep_testResult, result)
76         if errorInfo and errorInfo:
77             ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorInfo, errorInfo)
78             ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorPic, errorPic)
79         else:
80             ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorInfo, '')
81             ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorPic, '')
82 if __name__=='__main__':
83     p = ParseExcel()
84     print(p.getRowValue('126account',2))
85     print(p.getColumnValue('126account',3))
86     print(p.getCellOfValue('126account', 2, 3))

7.新建Log.py文件,用来记录代码运行日志

 
 1 import logging
 2 import time
 3 from config.VarConfig import *
 4 
 5 class Logger(object):
 6     '''
 7     封装的日志模块
 8     '''
 9     def __init__(self, logger, CmdLevel=logging.INFO, FileLevel=logging.INFO):
10         """
11 
12         :param logger:
13         :param CmdLevel:
14         :param FileLevel:
15         """
16         try:
17             self.logger = logging.getLogger(logger)
18             self.logger.setLevel(logging.DEBUG)  # 设置日志输出的默认级别
19             # 日志输出格式
20             fmt = logging.Formatter('%(asctime)s - %(filename)s:[%(lineno)s] - [%(levelname)s] - %(message)s')
21             # 日志文件名称
22             # self.LogFileName = os.path.join(conf.log_path, "{0}.log.txt".format(time.strftime("%Y-%m-%d")))# %H_%M_%S
23             currTime = time.strftime("%Y-%m-%d")
24             self.LogFileName = logPath+currTime+'.txt'
25             # 设置控制台输出
26             # sh = logging.StreamHandler()
27             # sh.setFormatter(fmt)
28             # sh.setLevel(CmdLevel)# 日志级别
29 
30             # 设置文件输出
31             fh = logging.FileHandler(self.LogFileName)
32             fh.setFormatter(fmt)
33             fh.setLevel(FileLevel)# 日志级别
34 
35             # self.logger.addHandler(sh)
36             self.logger.addHandler(fh)
37         except Exception as e:
38             raise e
39 
40 if __name__ == '__main__':
41     logger = Logger("fox",CmdLevel=logging.DEBUG, FileLevel=logging.DEBUG)
42     logger.logger.debug("debug")
43     logger.logger.log(logging.ERROR,'%(module)s %(info)s',{'module':'log日志','info':'error'}) #ERROR,log日志 error
业务操作功能模块

新建action文件夹,主要存储页面的各种操作,如点击操作,输入操作等1.文件夹下新建PageAction.py文件

 
  1 from util.ObjectMap import *
  2 from util.ClipboardUtil import Clipboard
  3 from util.KeyBoardUtil import KeyBoardKeys
  4 from util.WaitUntil import WaitUnit
  5 from util.DirAndTime import *
  6 from selenium import webdriver
  7 
  8 driver = None
  9 waitUtil = None
 10 # 打开浏览器
 11 def openBrowser(browser):
 12     global driver, waitUtil
 13     try:
 14         if browser.lower() =='ie':
 15             driver = webdriver.Ie(executable_path=iePath)
 16         elif browser.lower() == 'chrome':
 17             driver = webdriver.Chrome(executable_path=chromePath)
 18         else:
 19             # driver = webdriver.Firefox(executable_path=fireFox)
 20             driver = webdriver.Firefox()
 21     except Exception as e:
 22         raise e
 23     else:
 24         waitUtil = WaitUnit(driver) # driver 创建之后, 创建等待类实例对象
 25 
 26 # 浏览器窗口最大化
 27 def maximize_browser():
 28     try:
 29         driver.maximize_window()
 30     except Exception as e:
 31         raise e
 32 # 加载网址
 33 def loadUrl(url):
 34     try:
 35         driver.get(url)
 36     except Exception as e:
 37         raise e
 38 
 39 # 强制等待
 40 def sleep(sleepSeconds):
 41     try:
 42         import time
 43         time.sleep(sleepSeconds)
 44     except Exception as e:
 45         raise e
 46 # 清除输入框的内容
 47 def clear(by, locator):
 48     try:
 49         getElement(driver, by, locator).clear()
 50     except Exception as e:
 51         raise e
 52 # 输入框中输入内容
 53 def inputValue(by, locator, value):
 54     try:
 55         element = getElement(driver, by, locator)
 56         # element.click()
 57         element.send_keys(value)
 58     except Exception as e:
 59         raise e
 60 # 点击操作
 61 def clickBtn(by, locator):
 62     try:
 63         getElement(driver, by, locator).click()
 64     except Exception as e:
 65         raise e
 66 # 断言页面的title
 67 def assertTitle(titleStr):
 68     try:
 69         assert titleStr in driver.title, "%s not found in title!" % titleStr
 70     except AssertionError as e:
 71         raise AssertionError(e)
 72     except Exception as e:
 73         raise e
 74 
 75 # 断言目标字符串是否包含在页面源码中
 76 def assert_string_in_page_source(assertString):
 77     try:
 78         assert assertString in driver.page_source, "%s not found in page source!" % assertString
 79     except AssertionError as e:
 80         raise AssertionError(e)
 81     except Exception as e:
 82         raise e
 83 
 84 # 获取当前页面的title
 85 def getTitle():
 86     try:
 87         return driver.title
 88     except Exception as e:
 89         raise e
 90 
 91 # 获取页面源码
 92 def getPageSource():
 93     try:
 94         return driver.page_source
 95     except Exception as e:
 96         raise e
 97 # 切换到frame里面
 98 def switchToFrame(by, locator):
 99     try:
100         driver.switch_to.frame(getElement(driver, by, locator))
101     except Exception as e:
102         raise e
103 
104 # 跳到默认的frame
105 def switchToDefault():
106     try:
107         driver.switch_to.default_content()
108     except Exception as e:
109         raise e
110 
111 # 模拟ctrl+v键
112 def ctrlV(value):
113     try:
114         Clipboard.setText(value)
115         sleep(2)
116         KeyBoardKeys.twoKeys('ctrl', 'v')
117     except Exception as e:
118         raise e
119 
120 # 模拟tab键
121 def tabKey():
122     try:
123         KeyBoardKeys.oneKey('tab')
124     except Exception as e:
125         raise e
126 
127 # 模拟enter键
128 def enterKey():
129     try:
130         KeyBoardKeys.oneKey('enter')
131     except Exception as e:
132         raise e
133 
134 # 屏幕截图
135 def saveScreenShot():
136     pictureName = DirAndTime.CreatePicturePath() +'\\'+DirAndTime.getCurrentTime() + '.png'
137     try:
138         driver.get_screenshot_as_file(pictureName)
139     except Exception as e:
140         raise e
141     else:
142         return pictureName
143 
144 def waitPresenceOfElementLocated(by, locator):
145     '''
146     显示等待页面元素出现在DOM中,单并不一定可见
147     :param by:
148     :param locator:
149     :return:
150     '''
151     waitUtil.presenceOfElementLocated(by, locator)
152 
153 def waitFrameToBeAvailableAndSwitchToIt(by, locator):
154     '''
155     检查frame是否存在,存在就切换到frame中
156     :param by:
157     :param locator:
158     :return:
159     '''
160     waitUtil.frameToBeAvailableAndSwtichToIt(by, locator)
161 
162 def waitVisibiltyOfElementLocated(by, locator):
163     '''
164     显示等待页面元素出现在DOM中,并且可见
165     :param by:
166     :param locator:
167     :return:
168     '''
169     waitUtil.visibiltyOfElementLocated(by, locator)
170 
171 # 关闭浏览器
172 def quitBroswer():
173     try:
174         driver.quit()
175     except Exception as e:
176         raise e
177 if __name__=='__main__':
178     openBrowser('firefox')
179     loadUrl('http://www.baidu.com')
180     # inputValue('id', 'kw','python')
181     # clear('id', 'kw')
182     # inputValue('id', 'kw', 'python')
183     # clickBtn('id', 'su')
184     # sleep(3)
185     # title = getTitle()
186     # print(title)
187     # assertTitle('python')
188     # assert_string_in_page_source('python')
189     ctrlV('python')
项目数据文件设计

我们既然要实现关键字驱动的测试,无疑是通过关键字数据文件来控制代码的执行

新建testData文件夹,并新建126mailSend.xlsx文件。文件内容包括3个sheet页,分别为测试用例,登录,发送邮件

测试用例页

登录页

  发送邮件页

注意:表格中的关键字 需要和PageAction.py中的方法名字保持一致

项目配置模块

新建config目录,并新建VarConfig.py文件记录全局的目录及excel文件部分信息

 
 1 # 存储全局的变量
 2 import os
 3 
 4 # 项目根目录
 5 projectPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 6 # 截图目录
 7 exceptionPath = projectPath +r'\exceptionpictures'
 8 
 9 # 驱动存放路径, 需要自己根据自己电脑的驱动为止修改
10 iePath = ''
11 chromePath = ''
12 fireFox = ''
13 
14 # excel文件存放路径
15 excelPath = projectPath + r'\testData\126mailSend.xlsx'
16 # loh文件存放路径
17 logPath = projectPath + '\\log\\'
18 # 测试用例部分列对应的列号
19 testCase_testCaseName = 2
20 testCase_testStepName = 4
21 testCase_testIsExecute = 5
22 testCase_testRunEndTime = 6
23 testCase_testResult = 7
24 
25 # 用例步骤对应的列号
26 testStep_testNum = 1
27 testStep_testStepDescribe = 2
28 testStep_keyWord = 3
29 testStep_elementBy = 4
30 testStep_elementLocator = 5
31 testStep_operateValue = 6
32 testStep_testRunTime = 7
33 testStep_testResult = 8
34 testStep_testErrorInfo = 9
35 testStep_testErrorPic = 10
36 
37 
38 if __name__=='__main__':
39 
40     print(projectPath)
41     print(exceptionPath)
测试用例编写

前期所有的准备都已经完成,接下来我们开始编写测试用例

新建testCases文件夹,并新建Test126SendMailWithAttachment.py编写用例

 1 from util.ParseExcel import ParseExcel
 2 from config.VarConfig import *
 3 from action.PageAction import *
 4 import traceback
 5 from util.log import Logger
 6 import logging
 7 
 8 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO)
 9 p = ParseExcel()
10 sheetName = p.wb.sheetnames# 获取到excel的所有sheet名称
11 
12 def Test126MailSendWithAtt():
13     try:
14         testCasePassNum = 0
15 
16         requiredCase = 0
17         isExecuteColumnValues = p.getColumnValue(sheetName[0], testCase_testIsExecute)
18         # print(columnValues)
19         for index, value in enumerate(isExecuteColumnValues):
20             # print(index, value)
21             # 获取对应的步骤sheet名称
22             stepSheetName = p.getCellOfValue(sheetName[0],index+2, testCase_testStepName)
23             # print(stepSheetName)
24             if value.strip().lower() == 'y':
25                 requiredCase += 1
26                 testStepPassNum = 0
27                 print('开始执行测试用例"{}"'.format(stepSheetName))
28                 log.logger.info('开始执行测试用例"{}"'.format(stepSheetName))
29                 # 如果用例被标记为执行y,切换到对应的sheet页
30                 # 获取对应的sheet表中的总步骤数,关键字,定位方式,定位表达式,操作值
31                 # 步骤总数
32                 values = p.getColumnValue(stepSheetName, testStep_testNum) # 第一列数据
33                 stepNum = len(values)
34                 print(stepNum)
35                 for step in range(2, stepNum+2):
36                     rawValue = p.getRowValue(stepSheetName, step)
37                     # 执行步骤名称
38                     stepName = rawValue[testStep_testStepDescribe -2]
39                     # 关键字
40                     keyWord = rawValue[testStep_keyWord - 2]
41                     # 定位方式
42                     by = rawValue[testStep_elementBy - 2]
43                     # 定位表达式
44                     locator = rawValue[testStep_elementLocator - 2]
45                     # 操作值
46                     operateValue = rawValue[testStep_operateValue - 2]
47 
48                     if keyWord and by and locator and operateValue:
49                         func = keyWord + '(' +'"' +by +'"'+ ',' +'"'+locator+ '"'+',' +'"'+ operateValue + '"'+')'
50                     elif keyWord and by and locator and operateValue is None:
51                         func = keyWord + '(' +'"' +by +'"'+ ',' +'"'+locator+ '"'+')'
52 
53                     elif keyWord and operateValue and type(operateValue) == str and by is None and locator is None:
54                         func = keyWord + '(' +'"' + operateValue + '"' + ')'
55 
56                     elif keyWord and operateValue and type(operateValue) == int and by is None and locator is None:
57                         func = keyWord + '(' + str(operateValue) +')'
58 
59                     else:
60                         func = keyWord + '('+')'
61 
62                     try:
63                         # 执行测试步骤
64                         eval(func)
65                     except Exception:
66                         # 截图
67                         picPath = saveScreenShot()
68                         # 写回测试结果
69                         errorInfo = traceback.format_exc()
70                         p.writeTestResult(stepSheetName, step, 'Failed', errorInfo, picPath)
71                         print('步骤"{}"执行失败'.format(stepName))
72                         log.logger.info('步骤"{}"执行失败'.format(stepName))
73                         raise
74                     else:
75                         print('步骤"{}"执行通过'.format(stepName))
76                         log.logger.info('步骤"{}"执行通过'.format(stepName))
77                         # 标记测试步骤为pass
78                         p.writeTestResult(stepSheetName, step, 'Pass')
79                         testStepPassNum += 1
80                 # print('通过用例步数数:',testStepPassNum)
81                 if testStepPassNum == stepNum:
82                     # 标记测试用例sheet页的执行结果为pass
83                     p.writeCell(sheetName[0], index+2, testCase_testResult, 'Pass')
84                     testCasePassNum += 1
85                 else:
86                     p.writeCell(sheetName[0], index + 2, testCase_testResult, 'Failed')
87         print('共{}条用例,{}条需要被执行,本次执行通过{}条'.format(len(isExecuteColumnValues), requiredCase, testCasePassNum))
88         log.logger.info('共{}条用例,{}条需要被执行,本次执行通过{}条'.format(len(isExecuteColumnValues), requiredCase, testCasePassNum))
89     except Exception as e:
90         print(traceback.format_exc(e))
91         log.logger.info(traceback.format_exc(e))
92 
93 if __name__=='__main__':
94     Test126MailSendWithAtt()
加载用例

项目主目录下直接新建RunTest.py,用例运行测试用例

1 if __name__=='__main__':
2     from testCases.Test126SendMailWithAttachment import Test126MailSendWithAtt
3     Test126MailSendWithAtt()

项目总结
1.使用外部测试数据文件,使用Excel管理测试用例的集合和每个测试用例的所有测试步骤 ,实现一个文件中完成测试用例的维护

2.每个测试用例的测试结果在一个文件中查看和统计

3.通过定义关键字,操作元素的定位方式及定位表达式和操作值就可以实现每个测试用例步 骤的执行,可以更加灵活地实现自动化测试的需求

4.实现定位表达式和测试代码的分离,实现定位表达式直接在测试数据文件中进行维护。

5.框架提供日志功能,方便调试和监控自动化测试程序的执行

6.基于关键字测试框架,即使不懂开发技术的测试人员也可以实施自动化测试,便于在整个 测试团队中推广和使用自动化测试技术,降低自动化测试实施的技术门槛

7.基于关键字的方式,可以进行任意关键字的扩展,以满足更加复杂项目的自动化测试需求

运行框架
1.运行环境需要安装了python3.x+selenium2.x;第三方模块openpyxl,pypiwin32,
  win32api, win32con
2.本地已配置chrome/firefox/ie浏览器及对应版本驱动
3.需要修改Excel文件中对应的用户名和密码
4.直接运行RunTest.py文件即可执行整个框架
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

 文档获取方式

加入我的软件测试交流群:632880530免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)

这份文档,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

以上均可以分享,只需要你搜索vx公众号:程序员雨果,即可免费领取

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

make/makefile

目录 makefile介绍 什么是makefile 为什么要有makefile 编写makefile .PHONY 清理文件 时间问题 为什么不能总是执行 怎么判断程序是不是最新 修改单个对其他时间对其他时间的影响 make默认执行 makefile扩展 linux项目自动化构建工具-make/makefile make是一条命…

PHP网站源码 知识付费分站代理自助下单系统 自带多款模板

源码测评:功能很齐全,有可以对接的总站,应该是对接好就可以推广赚钱了,但是这种感觉能赚钱的就那么几个人,见仁见智吧! 截图演示: 转载自 https://www.qnziyw.cn/cmsmb/qtcms/3952.html

Three.js 实现简单的PCD加载器(可从本地读取pcd文件)【附完整代码】

1 功能实现 初始会显示我们之前 SfM 做出的点云,包括相机位置可以点击右上角加载你本地的PCD文件可以通过选择多个文件加载多个点云并显示在同一场景中可以通过左上角的控制界面查看/调整点云的属性,如点大小、颜色等可以通过右上角的控制界面选择旋转 …

Linux下内网穿透实现云原生观测分析工具的远程访问

📑前言 本文主要是Linux下内网穿透实现云原生观测分析工具的远程访问设置的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 &…

C++20 Text formatting

C20 Text formatting 格式化字符串&#xff0c; 和 python 类似。 std::formatter - cppreference.com string — Common string operations — Python 3.12.0 documentation 新格式库位于 <format> 头文件中。格式库基于 Python3 中的 str.format() 方法建模。格式…

一文6个步骤带你实现接口测试入门!

一、接口测试概述 1 什么是接口测试&#xff1a; 接口测试是测试系统组件间交互的一种测试。接口测试主要用于检测外部系统与系统之间&#xff0c;内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑…

直播会议一体机安卓主板_5G智能会议一体机双屏异显设计

5G直播会议一体机主板是专门为支持音视频输入输出而设计的&#xff0c;内置有安卓13系统&#xff0c;可兼容多种直播和会议软件。该产品可广泛应用于智能会议一体机、便携式直播设备、录播导播、无人机直播以及视频传输等多个领域。 这款主板采用了国产6纳米旗舰芯片紫光展锐T8…

ChatRule:基于知识图推理的大语言模型逻辑规则挖掘11.10

ChatRule&#xff1a;基于知识图推理的大语言模型逻辑规则挖掘 摘要引言相关工作初始化和问题定义方法实验 摘要 逻辑规则对于揭示关系之间的逻辑联系至关重要&#xff0c;这可以提高推理性能并在知识图谱&#xff08;KG&#xff09;上提供可解释的结果。虽然已经有许多努力&a…

前端 a链接 如何实现下载功能

目录 前言 标签 download a 标签链接下载的实现 1. 整体流程 2. 实现步骤 3. 类图 4. 代码示例 download 使用注意点 1. 同源 URL 的限制 2. 不能携带 Header Blob 转换 方法1. 用作 URL&#xff08;blob:&#xff09; 方法2. 转换为 base64&#xff08;data:&…

无人机航迹规划:五种最新智能优化算法(COA、SWO、KOA、GRO、LO)求解无人机路径规划MATLAB

一、五种算法&#xff08;LSO、SWO、KOA、GRO、LO&#xff09;简介 1、小龙虾优化算法COA 小龙虾优化算法&#xff08;Crayfsh optimization algorithm&#xff0c;COA&#xff09;由Jia Heming 等人于2023年提出&#xff0c;该算法模拟小龙虾的避暑、竞争和觅食行为&#xf…

数据分析实战 | 线性回归——女性身高与体重数据分析

目录 一、数据集及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型评价 九、模型调参 十、模型预测 实现回归分析类算法的Python第三方工具包比较常用的有statsmodels、statistics、scikit-learn等&#…

【CASS精品教程】cass 3d基于osgb三维模型生成等高线的两种方法

对于植被、房屋稀少的地方,可以基于osgb模型直接生成等高线。本文讲解在cass11.0 3d中基于osgb三维模型生成等高线的两种方法。 一、加载osgb三维模型 二、生成等高线 1. 绘制等高线 cass11版本提供了绘制单个等高线的功能。 点击【绘制等高线】,提示输入等高距。 输入固定…

MySQL的高阶语句

数据库的权限一般很小&#xff0c;工作中使用最多的场景就是查 排序、分组、子查询、视图、多表连接查询&#xff08;左连接、右连接、内连接&#xff09; create TABLE info ( id int(4) primary key, NAME varchar(5) not null, score decimal(5,2), address varchar(20)…

渗透测试必备工具--Metasploit(流程梳理与meterpreter权限分析)

目录 一、攻击前期准备必会的命令&#xff08;msf流程&#xff09; 1、启动&#xff1a;msfdb run 或者 msfconsole 2、Payload生成&#xff1a;msfvenom 3、查找相关模块&#xff1a;search 4、选择使用模块&#xff1a;use 5、返回上一层&#xff1a;back 6、查看需要…

猫罐头哪家好?宠物店自用的5款猫罐头推荐!猫咪嘎嘎炫~

亲爱的铲屎官们&#xff0c;你们是否会为猫咪选购猫罐头而感到烦恼&#xff1f;你们是否渴望了解哪些猫罐头在宠物界有着良好的口碑&#xff1f;猫罐头&#xff0c;作为猫咪日常饮食中的重要组成部分&#xff0c;其品质直接影响到猫咪的健康和幸福。 猫罐头哪家好&#xff1f;作…

go 引入包报错“构建约束排除‘D/...vendor/pkg包’”中所有的GO文件

解决方案&#xff1a; 方案一&#xff1a;没生效 go - 构建约束排除所有 Go 文件 - IT工具网 go modules - build constraints exclude all Go files in - Stack Overflow 方案二&#xff1a;生效&#xff0c;手动初始化创建一个目录 后续再研究原因&#xff0c;有明白的大…

边缘智能模型训练、推理的关键技术

目录 模型推理边缘智能模型推理的关键指标延迟&#xff08;Latency&#xff09;准确性&#xff08;Accuracy&#xff09;能效&#xff08;Energy&#xff09;隐私&#xff08;Privacy&#xff09;通讯/计算开销&#xff08;Comm/comp Overhead&#xff09; 关键技术模型压缩&am…

node插件MongoDB(三)—— 库mongoose 的使用和数据类型(一)

前言 提示&#xff1a;使用mongoose 的前提是你安装了node和 MongoDB。 mongoose 官网文档&#xff1a;http://mongoosejs.net/docs/index.html 文章目录 前言一、安装二、基本使用1. 打开bin目录的mongod.exe文件2. 基本使用的代码&#xff08;连接mongodb 服务&#xff09;3.…

LeetCode算法题解(回溯,难点)|LeetCode37. 解数独

LeetCode37. 解数独 题目链接&#xff1a;37. 解数独 题目描述&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分…

【C++ 学习 ㉟】- 异常详解

目录 一、C 异常处理的基本语法 1.1 - 抛出异常 1.2 - 检测和捕获异常 二、在函数调用链中异常栈展开的匹配原则 三、异常重新抛出 四、异常规范 五、C 标准异常体系 程序的错误大致可以分为以下三种&#xff1a; 语法错误&#xff1a;在编译和链接阶段就能发现&#xf…
最新文章