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

前言

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

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

一、项目功能

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

测试地址

https://mail.126.com/

二、项目目录

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

三、框架搭建

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

3.1框架主要功能模块

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

1 from selenium.webdriver.support.wait import WebDriverWait2 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 By2 from selenium.webdriver.support.wait import WebDriverWait3 from selenium.webdriver.support import expected_conditions as EC4 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 w2 import win32con3 4 class Clipboard(object):5 6     @staticmethod7     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 win32api2 import win32con3 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, date2 from config.VarConfig import *3 4 class DirAndTime(object):5     @staticmethod6     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_workbook2 from config.VarConfig import *3 from datetime import datetime, date4 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 logging2 import time3 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

3.2业务操作功能模块

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

1.文件夹下新建PageAction.py文件

1 from util.ObjectMap import *2 from util.ClipboardUtil import Clipboard3 from util.KeyBoardUtil import KeyBoardKeys4 from util.WaitUntil import WaitUnit5 from util.DirAndTime import *6 from selenium import webdriver7 8 driver = None9 waitUtil = None10 # 打开浏览器11 def openBrowser(browser):12     global driver, waitUtil13     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 e23     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 e32 # 加载网址33 def loadUrl(url):34     try:35         driver.get(url)36     except Exception as e:37         raise e38 39 # 强制等待40 def sleep(sleepSeconds):41     try:42         import time43         time.sleep(sleepSeconds)44     except Exception as e:45         raise e46 # 清除输入框的内容47 def clear(by, locator):48     try:49         getElement(driver, by, locator).clear()50     except Exception as e:51         raise e52 # 输入框中输入内容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 e60 # 点击操作61 def clickBtn(by, locator):62     try:63         getElement(driver, by, locator).click()64     except Exception as e:65         raise e66 # 断言页面的title67 def assertTitle(titleStr):68     try:69         assert titleStr in driver.title, "%s not found in title!" % titleStr70     except AssertionError as e:71         raise AssertionError(e)72     except Exception as e:73         raise e74 75 # 断言目标字符串是否包含在页面源码中76 def assert_string_in_page_source(assertString):77     try:78         assert assertString in driver.page_source, "%s not found in page source!" % assertString79     except AssertionError as e:80         raise AssertionError(e)81     except Exception as e:82         raise e83 84 # 获取当前页面的title85 def getTitle():86     try:87         return driver.title88     except Exception as e:89         raise e90 91 # 获取页面源码92 def getPageSource():93     try:94         return driver.page_source95     except Exception as e:96         raise e97 # 切换到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 os3 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 ParseExcel2 from config.VarConfig import *3 from action.PageAction import *4 import traceback5 from util.log import Logger6 import logging7 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文件即可执行整个框架

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

在这里插入图片描述

软件测试面试小程序

被百万人刷爆的软件测试题库!!!谁用谁知道!!!全网最全面试刷题小程序,手机就可以刷题,地铁上公交上,卷起来!

涵盖以下这些面试题板块:

1、软件测试基础理论 ,2、web,app,接口功能测试 ,3、网络 ,4、数据库 ,5、linux

6、web,app,接口自动化 ,7、性能测试 ,8、编程基础,9、hr面试题 ,10、开放性测试题,11、安全测试,12、计算机基础

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!  

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

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

相关文章

Docker基础操作

1.安装docker服务,配置镜像加速器 安装docker服务 清理缓存 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-enginesystemctl enable --now docker 脚…

GEE-PIE遥感大数据处理技术

随着航空、航天、近地空间等多个遥感平台的不断发展,近年来遥感技术突飞猛进。由此,遥感数据的空间、时间、光谱分辨率不断提高,数据量也大幅增长,使其越来越具有大数据特征。对于相关研究而言,遥感大数据的出现为其提…

机器学习知识点总结:什么是EM(最大期望值算法)

什么是EM(最大期望值算法) 在现实生活中,苹果百分百是苹果,梨百分白是梨。 生活中还有很多事物是概率分布,比如有多少人结了婚,又有多少人有工作, 如果我们想要调查人群中吸大麻者的比例呢?敏感问题很难得…

摩托车外廓尺寸检测软件

本系统为摩托车外廓尺寸检测软件,该系统共涉及两种测量方法:自动测量和手动测量,旨在测量出每一台摩托车的外廓尺寸,包括但不限于摩托车的车长、车宽、车高、轮距、前悬、后悬、前伸距等需要测量的参数,可通过运行软件…

redis基本介绍以及在node中使用

文章目录 引言一、什么是redis1. redis简介2. redis的特点3. redis的应用场景 二、redis在windows下安装1. 下载安装2.验证是否安装成功3. 配置环境变量 三、redis-cli常用命令介绍1. redis-cli2. keys *3. set key value4. get key5. exists key6. del key7. info8. flushdb9.…

2023年计算机设计大赛国三 数据可视化 (源码可分享)

2023年暑假参加了全国大学生计算机设计大赛,并获得了国家三等奖(国赛答辩出了点小插曲)。在此分享和记录本次比赛的经验。 目录 一、作品简介二、作品效果图三、设计思路四、项目特色 一、作品简介 本项目实现对农产品近期发展、电商销售、灾…

面试官问我Redis怎么测,我一脸懵逼!

有些测试朋友来问我,redis要怎么测试?首先我们需要知道,redis是什么?它能做什么? redis是一个key-value类型的高速存储数据库。 redis常被用做:缓存、队列、发布订阅等。 所以,“redis要怎么测试…

13 Web全栈 pnpm

什么是pnpm? 可以理解成performant npm缩写 速度快、节省磁盘空间的软件包管理器 特点 快速- pnpm比其他包管理器快2倍高效- node_modules中的文件链接自特定的内容寻址存储库支持monorepos- 内置支持单仓多包严格- pnpm默认创建了一个非平铺的node_modules 因此代…

UE4/5Niagara粒子特效学习(使用UE5.1,适合新手)

目录 创建空模板 创建粒子 粒子的基础属性 粒子的生命周期 颜色 大小设置 生成的位置 Skeletal Mesh Location的效果: Shape Location 添加速度 添加Noise力场 在生成中添加: 效果: ​编辑 在更新中添加: 效果&…

基础论文学习(1)——ViT

Vision Transformer(ViT) 模型架构是在 ICLR 2021 上作为会议论文发表的一篇研究论文中介绍的,题为“An Image is Worth 16*16 Words: Transformers for Image Recognition at Scale”。它由Neil Houlsby,Alexey Dosovitskiy和Goo…

常用获取威胁情报数据+信息溯源的平台

获取威胁情报数据 奇安信威胁分析平台深信服威胁情报中心360安全大脑腾讯哈勃分析系统绿盟威胁情报中心安全星图平台安天威胁情报中心VenusEye威胁情报中心VirustotalIBM X-Force威胁情报ThreatBookAlienVaultVirusScan多引擎在线扫描RiskIQThreatMiner 需要注意的是&#xff0…

php 系列题目,包含查看后端源代码

一、弱类型比较问题 原则: 1.字符串和数字比较,字符串回被转换成数字。 "admin" 0(true) admin被转换成数字,由于admin是字符串,转换失败,变成0 int(admin)0,所以比较结果是ture 2.混合字符串转…

【轴承故障诊断】用于轴承故障诊断的集中时频分析研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Go语言基础之函数

函数 Go语言中支持函数、匿名函数和闭包,并且函数在Go语言中属于“一等公民”。 函数定义 Go语言中定义函数使用func关键字,具体格式如下: func 函数名(参数)(返回值){函数体 }其中: 函数名:由字母、数字、下划线…

借助frp的xtcp+danted代理打通两边局域网p2p方式访问

最终效果 实现C内网所有设备借助c1内网代理访问B内网所有服务器 配置公网服务端A frps 配置frps.ini [common] # 绑定frp穿透使用的端口 bind_port 7000 # 使用token认证 authentication_method token token xxxx./frps -c frps.ini启动 配置service自启(可选) /etc/…

分布式核心知识以及常见微服务框架

分布式中的远程调用 在微服务架构中,通常存在多个服务之间的远程调用的需求。远程调用通常包含两个部分:序列化和通信协议。常见的序列化协议包括json、xml、 hession、 protobuf、thrift、text、 bytes等,目前主流的远程调用技术有基于HTTP…

C#小轮子:MiniExcel,快速操作Excel

文章目录 前言环境安装功能测试普通读写读新建Excel表格完全一致测试:成功大小写测试:严格大小写别名读测试:成功 写普通写别名写内容追加更新模板写 其它功能xlsx和CSV互转 前言 Excel的操作是我们最常用的操作,Excel相当于一个…

Unity 之NavMeshAgent 组件(导航和路径寻找的组件)

文章目录 **作用**:**属性和方法**:**用途**:**注意事项**: NavMeshAgent 是Unity引擎中用于导航和路径寻找的组件。它可以使游戏对象在场景中自动找到可行走的路径,并在避免障碍物的情况下移动到目标位置。 以下是关于…

从零玩转系列之微信支付实战PC端装修我的订单页面 | 技术创作特训营第一期

一、前言 欢迎来到本期的博客!本篇文章是 PC 端的结尾了,前面经历过九个章节到本章节刚刚好十章节感谢观看我的文章,那么接下来我们将要编写的是我的订单页面. GGBOM! 本篇完毕后将是 UniApp 的篇章感受移动端的诱惑 💗 本次为前端知识点如果不懂前段可以…

信号波形解读

can波形解读 实际波形 标准帧 发送数据 仲裁段 0x1AA 数据长度为8字节 内容为:0x41, 0x20, 0x38, 0x41, 0x00, 0x16, 0x00, 0x00 波特率 111K