快速教你如何搭建数据驱动自动化测试框架?

发表于:2019-3-25 08:51

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:linux超    来源:博客园

  前言
  说到数据驱动自动化测试,你会不会有这样的疑问:数据怎么管理?数据怎么才能驱动测试用例执行?到底怎么样才算数据驱动?那么本篇文章就教你如何进行数据驱动测试,相信你一定能对数据驱动自动化测试有一个不一样的理解,希望这篇文章能帮助还在懵懵懂懂的你了解数据驱动测试,如何来做数据驱动测试?那么就跟上我的脚步吧^_^
  项目介绍
  地址:https://www.126.com/
  1.实现126邮箱登录功能的验证
  2.实现126邮箱添加新联系人功能的验证
  框架分析
  1.新建config目录存放和业务无关的数据文件,比如PageElementLocator.ini 用来存放我们每个页面的定位元素表达式。
   [126mail_login]
  loginPage.frame=xpath>//div[@id='loginDiv']/iframe
  loginPage.username=xpath>//input[@name='email']
  loginPage.password=xpath>//input[@name='password']
  loginPage.loginBtn=xpath>//a[@id='dologin']
  [126mail_homePage]
  homePage.addressbook=id>_mail_tabitem_1_4text
  [126mail_addContactPage]
  addContactPage.newContact=xpath>//span[text()='新建联系人']
  addContactPage.newName=id>input_N
  addContactPage.newMail=xpath>//div[@id='iaddress_MAIL_wrap']//input[@class='nui-ipt-input']
  addContactPage.newMark=xpath>//span[@class='nui-chk-text']/preceding-sibling::span/b
  addContactPage.newPhone=xpath>//div[@id='iaddress_TEL_wrap']//input[@class='nui-ipt-input']
  addContactPage.newComment=id>input_DETAIL
  addContactPage.newCommit=xpath>//span[text()='确 定']
  2.有了上面元素的表达式我们又该如何查找我们的元素呢?这时候我们需要封装一套无论何种定位方式都可以使用的通用的查找方法。新建util目录(util目录主要存放和我们业务无关的公共方法),此目录下新建ObjectMap.py文件
   1 from selenium.webdriver.support.wait import WebDriverWait
  2
  3 def getElement(driver, by, locate):
  4     '''
  5     查找单一个元素
  6     :param driver: 浏览器驱动
  7     :param by: 定位方式,id, name, class, xpath...
  8     :param locate: 定位表达式
  9     :return: 元素
  10     '''
  11     try:
  12         element = WebDriverWait(driver, 30).until(lambda x :x.find_element(by, locate))
  13     except Exception as e:
  14         raise e
  15     else:
  16         return element
  17 def getElelments(driver, by, locate):
  18     '''
  19     查找一组元素
  20     :param driver: 浏览器驱动
  21     :param by: 定位方式
  22     :param locate: 定位表达式
  23     :return: 一组元祖组成的列表
  24     '''
  25     try:
  26         elements = WebDriverWait(driver, 30).until(lambda x :x.find_elements(by, locate))
  27     except Exception as e:
  28         raise e
  29     else:
  30         return elements
  31 if __name__=='__main__':
  32     from selenium import webdriver
  33     driver = webdriver.Firefox()
  34     driver.get('http://www.baidu.com')
  35     inputBaidu = getElement(driver, 'id', 'kw')
  36     inputBaidu.send_keys('python')
  3.定位表达式有了,查找元素的方法封装好了,下面我们需要解析配置文件,获取配置文件中我们需要的定位方式和定位表达式。同样在util目下新建ParseConfigurationFile.py文件用来解析配置文件
   1 from config.varCondig import pageElementLocatorPath
  2 import configparser
  3
  4 class ParseConfigFile(object):
  5     '''
  6     解析ini配置文件
  7     '''
  8     def __init__(self):
  9         try:
  10             self.cf = configparser.ConfigParser() # 获取配置文件对象
  11             self.cf.read(pageElementLocatorPath, encoding='utf-8') # 加载配置文件到内存中
  12         except Exception as e:
  13             raise e
  14
  15     def getItemsSection(self, sectionName):
  16         '''
  17         获取section下面所有section的键值
  18         :param sectionName:
  19         :return:
  20         '''
  21         try:
  22             vlaues = dict(self.cf.items(sectionName))
  23         except Exception as e:
  24             raise e
  25         else:
  26             return vlaues
  27
  28     def getElementValue(self, sectionName, optionName):
  29         try:
  30             locator = self.cf.get(sectionName, optionName).split('>')
  31         except Exception as e:
  32             raise e
  33         else:
  34             return locator # 获取option键对应的value
  35
  36     def getAllSections(self):
  37         try:
  38             allsections = self.cf.sections()
  39         except Exception as e:
  40             raise e
  41         else:
  42             return allsections # 所有的sections返回值是个列表
  43
  44     def getAllOptions(self, section):
  45         try:
  46             options = self.cf.options(section)
  47         except Exception as e:
  48             raise e
  49         else:
  50             return options # 某个section下面的键
  51
  52 if __name__=='__main__':
  53     cf = ParseConfigFile()
  54     locator = cf.getElementValue('126mail_login','loginPage.username')
  55     # print(locator)
  56     print(cf.getItemsSection('126mail_login'))
  57     print(cf.getAllSections())
  58     print(cf.getAllOptions('126mail_addContactPage'))
  4.我们获取到了每一个元素的定位方式和定位表达式,接下来我们又该如何查找到我们的元素对象呢?这时候我们会想到PO(pageObject)设计模式,把我们每一个page页面的元素保存到对应页面的文件中。
  新建pageObjects目录,在此目录下分别创建HomePage.py LoginPage.py NewContact.py 三个页面文件
  LoginPage.py文件存放我们登录页面所需要的元素, HomePage.py 存放登录成功后首页所需要的页面元素, NewContact.py文件存放我们添加联系人页面所需要的元素。
   1 from util.ObjectMap import *
  2 from util.ParseConfigurationFile import ParseConfigFile
  3 class LoginPage(object):
  4     '''
  5     登录页面所有的操作元素对象
  6     '''
  7     def __init__(self, driver):
  8         self.driver = driver
  9         self.cf = ParseConfigFile()
  10
  11     def switchToFrame(self):
  12         '''
  13         切换到frame中
  14         :return:
  15         '''
  16         by, locator = self.cf.getElementValue('126mail_login', 'loginPage.frame')
  17         try:
  18             self.driver.switch_to.frame(getElement(self.driver, by,locator))
  19         except Exception as e:
  20             raise e
  21
  22     def switchToDefaultFrame(self):
  23         '''
  24         跳出frame
  25         :return:
  26         '''
  27         try:
  28             self.driver.switch_to.default_content()
  29         except Exception as e:
  30             raise e
  31
  32     def userNameObj(self): # 用户名输入框
  33         by, locator = self.cf.getElementValue('126mail_login', 'loginPage.username')
  34
  35         username = getElement(self.driver, by, locator)
  36         return username
  37
  38     def passwordObj(self): # 密码输入框
  39         by, locator = self.cf.getElementValue('126mail_login', 'loginPage.password')
  40
  41         password = getElement(self.driver, by, locator)
  42         return password
  43
  44     def loginBtnObj(self): # 登录按钮
  45         by, locator = self.cf.getElementValue('126mail_login', 'loginPage.loginBtn')
  46
  47         loginbtn = getElement(self.driver, by, locator)
  48         return loginbtn
  49
  50 if __name__=='__main__':
  51     from selenium import webdriver
  52     import time
  53     driver = webdriver.Firefox()
  54     driver.get('https://mail.126.com')
  55     login = LoginPage(driver)
  56     time.sleep(5)
  57     login.switchToFrame()
  58     login.userNameObj().send_keys('linuxxiaochao')
  59     login.passwordObj().send_keys('xiaochao11520')
  60     login.loginBtnObj().click()
  61     login.switchToDefaultFrame()
  62     driver.quit()
 
   1 from util.ObjectMap import * #查找元素的模块
  2 from util.ParseConfigurationFile import ParseConfigFile
  3
  4 class HomePage(object):
  5
  6     def __init__(self, driver):
  7         self.driver = driver
  8         self.cf = ParseConfigFile()
  9
  10     def addressLink(self):
  11         '''
  12         通讯录菜单对象
  13         :return:
  14         '''
  15         by, locator = self.cf.getElementValue('126mail_homePage','homePage.addressbook')
  16
  17         elementObj = getElement(self.driver, by, locator)
  18         return elementObj
  19
  20 if __name__=='__main__':
  21     from selenium import webdriver
  22     from pageObjects.LoginPage import LoginPage
  23     import time
  24     driver = webdriver.Firefox()
  25     driver.get('https://mail.126.com')
  26     login = LoginPage(driver)
  27     homePage = HomePage(driver)
  28     time.sleep(5)
  29     login.switchToFrame()
  30     login.userNameObj().send_keys('linuxxiaochao')
  31     login.passwordObj().send_keys('xiaochao11520')
  32     login.loginBtnObj().click()
  33     login.switchToDefaultFrame()
  34     time.sleep(3)
  35     homePage.addressLink().click()
  36     time.sleep(10)
  37     driver.quit()
 
   1 from util.ParseConfigurationFile import ParseConfigFile
  2 from util.ObjectMap import *
  3
  4 class AddContactPage(object):
  5     '''
  6     添加联系人页面所有操作元素对象
  7     '''
  8     def __init__(self, driver):
  9         self.driver = driver
  10         self.cf = ParseConfigFile()
  11
  12     def newContact(self): # 新建联系人
  13         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newContact')
  14
  15         element = getElement(self.driver, by, locator)
  16         return element
  17
  18     def addName(self): # 姓名输入框
  19         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newName')
  20
  21         element = getElement(self.driver, by, locator)
  22         return element
  23
  24     def addMail(self): # 电子邮件输入框
  25         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newMail')
  26
  27         element = getElement(self.driver, by, locator)
  28         return element
  29
  30     def markStar(self): # 设为星际联系人
  31         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newMark')
  32
  33         element = getElement(self.driver, by, locator)
  34         return element
  35
  36     def addPhone(self): # 手机号码输入框
  37         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newPhone')
  38
  39         element = getElement(self.driver, by, locator)
  40         return element
  41
  42     def addContent(self): # 备注
  43         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newComment')
  44
  45         element = getElement(self.driver, by, locator)
  46         return element
  47
  48     def clickCommitBtn(self): # 确定按钮
  49         by, locator = self.cf.getElementValue('126mail_addContactPage', 'addContactPage.newCommit')
  50
  51         element = getElement(self.driver, by, locator)
  52         return element
  53
  54 if __name__=='__main__':
  55     from selenium import webdriver
  56     import time
  57     from pageObjects.HomePage import HomePage
  58     from appModules.LoginAction import LoginAction
  59
  60     driver = webdriver.Firefox()
  61     driver.get('https://mail.126.com')
  62     time.sleep(3)
  63     # 登录
  64     LoginAction.login(driver,'linuxxiaochao', 'xiaochao11520')
  65     # 主页面
  66     homepage = HomePage(driver)
  67     homepage.addressLink().click()
  68     time.sleep(5)
  69     # 添加联系人页面
  70     addcontact = AddContactPage(driver)
  71     addcontact.newContact().click()
  72     time.sleep(2)
  73     addcontact.addName().send_keys('test')
  74     addcontact.addMail().send_keys('13691579846@qq.com')
  75     addcontact.addPhone().send_keys('13691579846')
  76     addcontact.addContent().send_keys('ceshi')
  77     addcontact.markStar().click()
  78     time.sleep(3)
  79     addcontact.clickCommitBtn().click()
  5.接下来考虑,我们的框架需要数据驱动?那么我们该怎么用数据驱动我们测试呢?是不是应该通过修改测试数据能够控制我们用例的执行还是不执行呢?没错,大概就是这个样子,那我们又该如何来设计我们的数据呢?大概最好的办法就是使用excel文件来存储或者如果你有能力可以考虑使用数据库来存储,看下我们的表格是什么样子。
  新建testData目录存放我们测试过程中所有和业务数据相关的数据文件,并新建126MailContact.xlsx 文件,文件中分两个sheet 126account 和 126contact 分别存放登录和联系人数据

  6.通过上面的表我们可以看到两列数据“是否执行”,没错了我们就是通过这来控制数据是否驱动用例执行的,用例执行后写入测试是否通过和测试执行时间(代码中我没加写入时间,感兴趣的自己在用例只加就行了)
  7.有了这些数据我们又该如何读取到这些数据应用到我们的用例中呢?在util目录下新建ParseExcel.py 用来解析excel文件
   1 from openpyxl import load_workbook
  2 from config.varCondig import *
  3 class ParseExcel(object):
  4     '''
  5     解析excel文件的封装
  6     '''
  7     def __init__(self):
  8         # 加载excel文件到内存
  9         self.wb = load_workbook(testExcelValuePath)
  10
  11     def getRowValue(self, sheetName, rawNo):
  12         '''
  13         获取某一行的数据
  14         :param sheetName:
  15         :param rawNo:
  16         :return: 列表
  17         '''
  18         sh = self.wb[sheetName]
  19         rowValueList = []
  20         for y in range(2, sh.max_column+1):
  21             value = sh.cell(rawNo,y).value
  22             rowValueList.append(value)
  23         return rowValueList
  24     def getColumnValue(self, sheetName, colNo):
  25         '''
  26         获取某一列的数据
  27         :param sheetName:
  28         :param colNo:
  29         :return: 列表
  30         '''
  31         sh = self.wb[sheetName]
  32         colValueList = []
  33         for x in range(2, sh.max_row +1):
  34             value = sh.cell(x, colNo).value
  35             colValueList.append(value)
  36         return colValueList
  37
  38     def getCellOfValue(self, sheetName, rowNo, colNo):
  39         '''
  40         获取某一个单元格的数据
  41         :param sheetName:
  42         :param rowNo:
  43         :param colNo:
  44         :return: 字符串
  45         '''
  46         sh = self.wb[sheetName]
  47         value = sh.cell(rowNo, colNo).value
  48         return value
  49     def writeCell(self, sheetName, rowNo, colNo, value):
  50         '''
  51         向某个单元格写入数据
  52         :param rowNo: 行号
  53         :param colNo: 列号
  54         :param value:
  55         :return: 无
  56         '''
  57         sh = self.wb[sheetName]
  58         sh.cell(rowNo, colNo).value = value
  59         self.wb.save(testExcelValuePath)
  60 if __name__=='__main__':
  61     p = ParseExcel()
  62     print(p.getRowValue('126account',2))
  63     print(p.getColumnValue('126account',3))
  64     print(p.getCellOfValue('126account', 2, 3))
  8.在config目录下新建varConfig.py文件来存储一些目录信息和数据表对应的列号
   1 import os
  2
  3 # print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  4 # 项目目录
  5 parentDirPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  6 # init文件路径
  7 pageElementLocatorPath = parentDirPath+r'\config\PageElementLocator.ini'
  8 # excel文件路径
  9 testExcelValuePath = parentDirPath+r'\testData\126MailContact.xlsx'
  10 # 日志文件存放路径
  11 logPath = parentDirPath + r'\log'
  12
  13 # 126username 表,每列对用的序号
  14 account_userName = 2
  15 account_passWord = 3
  16 account_dataBook = 4
  17 account_isExecute = 5
  18 account_testResult = 6
  19
  20 # 126联系人表,每列对应的序号
  21 contact_contactName = 2
  22 contact_contactMail = 3
  23 contact_contactStar = 4
  24 contact_contactPhone = 5
  25 contact_contactComment = 6
  26 contact_contactKeyWords = 7
  27 contact_contactIsExcute = 8
  28 contact_contactExcuteTime = 9
  29 contact_contactTestResult = 10
  30
  31 if __name__=='__main__':
  32
  33     print(pageElementLocatorPath)
  34     print(testExcelValuePath)
  35     print(logPath)
  9.所有的数据已经准备完全,我们可以编写用例了把?等等! 我们是不是要考虑把业务功能封装一下呢?这样也方便以后依赖这些功能的用例直接调用!新建目录appModules 此目录用来存放业务功能代码,
  目录下新建LoginAction.py 和 AddContact.py 文件
   1 # 封装登录方法
  2
  3 from pageObjects.LoginPage import LoginPage
  4 class LoginAction(object):
  5     def __init__(self):
  6         pass
  7
  8     @staticmethod #
  9     def login(driver, username, password):
  10         '''
  11         登录场景
  12         :param driver:
  13         :param username:
  14         :param password:
  15         :return:
  16         '''
  17         login = LoginPage(driver)
  18         login.switchToFrame()
  19         login.userNameObj().send_keys(username)
  20         login.passwordObj().send_keys(password)
  21         login.loginBtnObj().click()
  22         login.switchToDefaultFrame()
  23
  24 if __name__=='__main__':
  25     from selenium import webdriver
  26     driver = webdriver.Firefox()
  27     driver.get('https://mail.126.com')
  28     LoginAction.login(driver, 'linux', 'chao')
  
1 from pageObjects.HomePage import HomePage
  2 from pageObjects.NewContact import AddContactPage
  3 from selenium.webdriver.support.wait import WebDriverWait
  4 from selenium.webdriver.support import expected_conditions as EC
  5 from util.ParseConfigurationFile import ParseConfigFile
  6
  7 class NewContactPersonAction(object):
  8     def __init__(self):
  9         pass
  10
  11     @staticmethod
  12     def addressLink(driver):
  13         '''
  14         点击通讯录按钮
  15         :param driver:
  16         :return:
  17         '''
  18         homePage = HomePage(driver)
  19         # 点击通讯录
  20         homePage.addressLink().click()
  21     @staticmethod
  22     def addContact(driver, contactName, contactMail, isSatr, contactPhone, contactComment):
  23         '''
  24         添加联系人场景
  25         :param driver:
  26         :param contactName:
  27         :param contactMail:
  28         :param isSatr:
  29         :param contactPhone:
  30         :param contactComment:
  31         :return:
  32         '''
  33         # 点击新建联系人
  34         addContact = AddContactPage(driver)
  35         # 调试的时候这边有时候会报错。点击不到[新建联系人]这个按钮,所以加了一个显示等待
  36         by, locator = ParseConfigFile().getElementValue('126mail_addContactPage', 'addContactPage.newContact')
  37         WebDriverWait(driver, 30).until(EC.element_to_be_clickable((by, locator)))
  38         addContact.newContact().click()
  39         if contactName:
  40             # 非必填项
  41             addContact.addName().send_keys(contactName)
  42         # 必填项
  43         addContact.addMail().send_keys(contactMail)
  44         if isSatr == '是':
  45             addContact.markStar().click()
  46         if contactPhone:
  47             addContact.addPhone().send_keys(contactPhone)
  48         if contactComment:
  49             addContact.addContent().send_keys(contactComment)
  50         addContact.clickCommitBtn().click()
  51
  52 if __name__=='__main__':
  53     from appModules.LoginAction import LoginAction
  54     import time
  55     from selenium import webdriver
  56     driver = webdriver.Firefox()
  57     driver.get('https://mail.126.com')
  58     time.sleep(5)
  59     LoginAction.login(driver, 'linux', 'chao')
  60     NewContactPersonAction.addressLink(driver)
  61     NewContactPersonAction.addContact(driver, '','123456@qq.com', '是', '','')
  62     time.sleep(5)
  63     driver.quit()
  10.我们的大体框架就已经搭建完成了,貌似还少了个执行日志,我们在util目录下新建log.py封装日志模块, 加入到我们想加入的任何地方(用例中我就加了几条,可以自己任意加在想加的位置)
   1 import logging
  2 import time
  3 from config.varCondig 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         self.logger = logging.getLogger(logger)
  17         self.logger.setLevel(logging.DEBUG)  # 设置日志输出的默认级别
  18         # 日志输出格式
  19         fmt = logging.Formatter('%(asctime)s - %(filename)s:[%(lineno)s] - [%(levelname)s] - %(message)s')
  20         # 日志文件名称
  21         # self.LogFileName = os.path.join(conf.log_path, "{0}.log.txt".format(time.strftime("%Y-%m-%d")))# %H_%M_%S
  22         currTime = time.strftime("%Y-%m-%d")
  23         self.LogFileName = logPath+r'\log'+currTime+'.txt'
  24         # 设置控制台输出
  25         # sh = logging.StreamHandler()
  26         # sh.setFormatter(fmt)
  27         # sh.setLevel(CmdLevel)# 日志级别
  28
  29         # 设置文件输出
  30         fh = logging.FileHandler(self.LogFileName)
  31         fh.setFormatter(fmt)
  32         fh.setLevel(FileLevel)# 日志级别
  33
  34         # self.logger.addHandler(sh)
  35         self.logger.addHandler(fh)
  36
  37 if __name__ == '__main__':
  38     logger = Logger("fox",CmdLevel=logging.DEBUG, FileLevel=logging.DEBUG)
  39     logger.logger.debug("debug")
  40     logger.logger.log(logging.ERROR,'%(module)s %(info)s',{'module':'log日志','info':'error'}) #ERROR,log日志 error
 
      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号