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

前言

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

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

一、项目功能

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

测试地址

https://mail.126.com/

二、项目目录

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

三、框架搭建

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

3.1框架主要功能模块

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

 
  1. 1 from selenium.webdriver.support.wait import WebDriverWait

  2. 2

  3. 3

  4. 4 def getElement(driver, by, locator):

  5. 5 '''

  6. 6 查找单一元素

  7. 7 :param driver:

  8. 8 :param by:

  9. 9 :param locator:

  10. 10 :return: 元素对象

  11. 11 '''

  12. 12 try:

  13. 13 element = WebDriverWait(driver, 30).until(lambda x : x.find_element(by, locator))

  14. 14 except Exception as e:

  15. 15 raise e

  16. 16 else:

  17. 17 return element

  18. 18

  19. 19 def getElements(driver, by, locator):

  20. 20 '''

  21. 21 获取一组元素

  22. 22 :param driver:

  23. 23 :param by:

  24. 24 :param locator:

  25. 25 :return: 一组元素对象

  26. 26 '''

  27. 27 try:

  28. 28 elements = WebDriverWait(driver, 30).until(lambda x : x.find_element(by, locator))

  29. 29 except Exception as e:

  30. 30 raise e

  31. 31 else:

  32. 32 return elements

  33. 33

  34. 34

  35. 35 if __name__=="__main__":

  36. 36 from selenium import webdriver

  37. 37 import time

  38. 38

  39. 39 driver = webdriver.Firefox()

  40. 40 driver.get('https://mail.126.com')

  41. 41 time.sleep(5)

  42. 42 driver.switch_to.frame(getElement(driver, 'xpath', "//div[@id='loginDiv']/iframe"))

  43. 43 username = getElement(driver, 'xpath', "//input[@name='email']")

  44. 44 username.send_keys('linuxxiaochao')

  45. 45 driver.switch_to.default_content()

  46. 46 driver.quit()

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

 
  1. 1 from selenium.webdriver.common.by import By

  2. 2 from selenium.webdriver.support.wait import WebDriverWait

  3. 3 from selenium.webdriver.support import expected_conditions as EC

  4. 4

  5. 5 class WaitUnit(object):

  6. 6 def __init__(self, driver):

  7. 7 self.byDic = {

  8. 8 'id': By.ID,

  9. 9 'name': By.NAME,

  10. 10 'class_name': By.CLASS_NAME,

  11. 11 'xpath': By.XPATH,

  12. 12 'link_text': By.LINK_TEXT

  13. 13 }

  14. 14 self.driver = driver

  15. 15 self.wait = WebDriverWait(self.driver, 50)

  16. 16

  17. 17 def presenceOfElementLocated(self, by, locator):

  18. 18 '''

  19. 19 显示等待某个元素出现在dom中,不一定可见,存在返回元素对象

  20. 20 :param by:

  21. 21 :param locator:

  22. 22 :return:

  23. 23 '''

  24. 24 try:

  25. 25 if by.lower() in self.byDic:

  26. 26 self.wait.until(EC.presence_of_element_located((self.byDic[by.lower()], locator)))

  27. 27 else:

  28. 28 raise TypeError('未找到定位方式,请确保定位方式正确')

  29. 29 except Exception as e:

  30. 30 raise e

  31. 31

  32. 32 def frameToBeAvailableAndSwtichToIt(self, by, locator):

  33. 33 '''

  34. 34 检查frame是否存在,存在就切换到frame中

  35. 35 :param by:

  36. 36 :param locator:

  37. 37 :return:

  38. 38 '''

  39. 39 try:

  40. 40 if by.lower() in self.byDic:

  41. 41 self.wait.until(EC.frame_to_be_available_and_switch_to_it((self.byDic[by.lower()], locator)))

  42. 42 else:

  43. 43 raise TypeError('未找到定位方式,请确保定位方式正确')

  44. 44 except Exception as e:

  45. 45 raise e

  46. 46 def visibiltyOfElementLocated(self, by, locator):

  47. 47 '''

  48. 48 显示等待页面元素出现在dom中, 并且可见, 存在则返回该元素对象

  49. 49 :param by:

  50. 50 :param locator:

  51. 51 :return:

  52. 52 '''

  53. 53 try:

  54. 54 if by.lower() in self.byDic:

  55. 55 self.wait.until(EC.visibility_of_element_located((self.byDic[by.lower()], locator)))

  56. 56 else:

  57. 57 raise TypeError('未找到定位方式,请确保定位方式正确')

  58. 58 except Exception as e:

  59. 59 raise e

  60. 60

  61. 61 if __name__=='__main__':

  62. 62 from selenium import webdriver

  63. 63 from util.ObjectMap import *

  64. 64 driver = webdriver.Firefox()

  65. 65 driver.get('https://mail.126.com')

  66. 66

  67. 67 wait = WaitUnit(driver)

  68. 68 wait.frameToBeAvailableAndSwtichToIt('xpath', "//div[@id='loginDiv']/iframe")

  69. 69 wait.visibiltyOfElementLocated('xpath', "//input[@name='email']")

  70. 70 uname = getElement(driver, 'xpath', "//input[@name='email']")

  71. 71 uname.send_keys('python')

  72. 72 driver.quit()

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

 
  1. 1 import win32clipboard as w

  2. 2 import win32con

  3. 3

  4. 4 class Clipboard(object):

  5. 5

  6. 6 @staticmethod

  7. 7 def getText():

  8. 8 '''

  9. 9 获取剪切板的内容

  10. 10 :return:

  11. 11 '''

  12. 12

  13. 13 try:

  14. 14 # 打开剪切板

  15. 15 w.OpenClipboard()

  16. 16 # 读取数据

  17. 17 value = w.GetClipboardData(win32con.CF_TEXT)

  18. 18 # 关闭剪切板

  19. 19 w.CloseClipboard()

  20. 20 except Exception as e:

  21. 21 raise e

  22. 22 else:

  23. 23 return value

  24. 24

  25. 25 @staticmethod

  26. 26 def setText(value):

  27. 27 '''

  28. 28 设置剪切板内容

  29. 29 :return:

  30. 30 '''

  31. 31 try:

  32. 32 w.OpenClipboard()# 打开剪切板

  33. 33 w.EmptyClipboard()# 清空剪切板

  34. 34 w.SetClipboardData(win32con.CF_UNICODETEXT, value) # 设置内容

  35. 35 w.CloseClipboard() # 关闭

  36. 36 except Exception as e:

  37. 37 raise e

  38. 38

  39. 39 if __name__=='__main__':

  40. 40 from selenium import webdriver

  41. 41

  42. 42 value = 'python'

  43. 43 driver = webdriver.Firefox()

  44. 44 driver.get('http://www.baidu.com')

  45. 45 query = driver.find_element_by_id('kw')

  46. 46 Clipboard.setText(value)

  47. 47 clValue = Clipboard.getText()

  48. 48 query.send_keys(clValue.decode('utf-8'))

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

 
  1. 1 import win32api

  2. 2 import win32con

  3. 3

  4. 4 class KeyBoardKeys(object):

  5. 5 '''

  6. 6 模拟键盘

  7. 7 '''

  8. 8 # 键盘编码

  9. 9 vk_code ={

  10. 10 'enter':0x0D,

  11. 11 'tab' : 0x09,

  12. 12 'ctrl':0x11,

  13. 13 'v':0x56

  14. 14 }

  15. 15 @staticmethod

  16. 16 def keyDown(keyName):

  17. 17 '''

  18. 18 模拟按下键

  19. 19 :param keyName:

  20. 20 :return:

  21. 21 '''

  22. 22 try:

  23. 23 win32api.keybd_event(KeyBoardKeys.vk_code[keyName],0,0,0)

  24. 24 except Exception as e:

  25. 25 raise e

  26. 26 @staticmethod

  27. 27 def keyUp(keyName):

  28. 28 '''

  29. 29 释放键

  30. 30 :param keyName:

  31. 31 :return:

  32. 32 '''

  33. 33 try:

  34. 34 win32api.keybd_event(KeyBoardKeys.vk_code[keyName],0,win32con.KEYEVENTF_KEYUP,0)

  35. 35 except Exception as e:

  36. 36 raise e

  37. 37 @staticmethod

  38. 38 def oneKey(key):

  39. 39 '''

  40. 40 模拟当个按键

  41. 41 :param key:

  42. 42 :return:

  43. 43 '''

  44. 44 try:

  45. 45 KeyBoardKeys.keyDown(key)

  46. 46 KeyBoardKeys.keyUp(key)

  47. 47 except Exception as e:

  48. 48 raise e

  49. 49

  50. 50 @staticmethod

  51. 51 def twoKeys(key1, key2):

  52. 52 '''

  53. 53 模拟组合按键

  54. 54 :param key1:

  55. 55 :param key2:

  56. 56 :return:

  57. 57 '''

  58. 58 try:

  59. 59 KeyBoardKeys.keyDown(key1)

  60. 60 KeyBoardKeys.keyDown(key2)

  61. 61 KeyBoardKeys.keyUp(key1)

  62. 62 KeyBoardKeys.keyUp(key2)

  63. 63 except Exception as e:

  64. 64 raise e

  65. 65

  66. 66 if __name__=='__main__':

  67. 67 from selenium import webdriver

  68. 68

  69. 69 driver = webdriver.Firefox()

  70. 70 driver.get('http://www.baidu.com')

  71. 71 driver.find_element_by_id('kw').send_keys('python')

  72. 72 KeyBoardKeys.oneKey('enter')

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

 
  1. 1 from datetime import datetime, date

  2. 2 from config.VarConfig import *

  3. 3

  4. 4 class DirAndTime(object):

  5. 5 @staticmethod

  6. 6 def getCurrentDate():

  7. 7 '''

  8. 8 获取当前日期

  9. 9 :return:

  10. 10 '''

  11. 11 try:

  12. 12 currentDate = date.today()

  13. 13 except Exception as e:

  14. 14 raise e

  15. 15 else:

  16. 16 return str(currentDate)

  17. 17 @staticmethod

  18. 18 def getCurrentTime():

  19. 19 '''

  20. 20 获取当前时间

  21. 21 :return:

  22. 22 '''

  23. 23 try:

  24. 24 Time = datetime.now()

  25. 25 currentTime = Time.strftime('%H_%M_%S')

  26. 26 except Exception as e:

  27. 27 raise e

  28. 28 else:

  29. 29 return currentTime

  30. 30 @staticmethod

  31. 31 def CreatePicturePath():

  32. 32 '''

  33. 33 创建图片存放路径路径

  34. 34 :return:

  35. 35 '''

  36. 36 try:

  37. 37

  38. 38 picturePath = os.path.join(exceptionPath , DirAndTime.getCurrentDate())

  39. 39 if not os.path.exists(picturePath):

  40. 40 os.makedirs(picturePath) # 生成多级目录

  41. 41 except Exception as e:

  42. 42 raise e

  43. 43 else:

  44. 44 return picturePath

  45. 45

  46. 46 if __name__=='__main__':

  47. 47 print(DirAndTime.getCurrentDate())

  48. 48 print(DirAndTime.getCurrentTime())

  49. 49 print(DirAndTime.CreatePicturePath())

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

 
  1. 1 from openpyxl import load_workbook

  2. 2 from config.VarConfig import *

  3. 3 from datetime import datetime, date

  4. 4

  5. 5 class ParseExcel(object):

  6. 6 '''

  7. 7 解析excel文件的封装

  8. 8 '''

  9. 9 def __init__(self):

  10. 10 # 加载excel文件到内存

  11. 11 self.wb = load_workbook(excelPath)

  12. 12

  13. 13 def getRowValue(self, sheetName, rawNo):

  14. 14 '''

  15. 15 获取某一行的数据

  16. 16 :param sheetName:

  17. 17 :param rawNo:

  18. 18 :return: 列表

  19. 19 '''

  20. 20 sh = self.wb[sheetName]

  21. 21 rowValueList = []

  22. 22 for y in range(2, sh.max_column+1):

  23. 23 value = sh.cell(rawNo,y).value

  24. 24 rowValueList.append(value)

  25. 25 return rowValueList

  26. 26 def getColumnValue(self, sheetName, colNo):

  27. 27 '''

  28. 28 获取某一列的数据

  29. 29 :param sheetName:

  30. 30 :param colNo:

  31. 31 :return: 列表

  32. 32 '''

  33. 33 sh = self.wb[sheetName]

  34. 34 colValueList = []

  35. 35 for x in range(2, sh.max_row +1):

  36. 36 value = sh.cell(x, colNo).value

  37. 37 colValueList.append(value)

  38. 38 return colValueList

  39. 39

  40. 40 def getCellOfValue(self, sheetName, rowNo, colNo):

  41. 41 '''

  42. 42 获取某一个单元格的数据

  43. 43 :param sheetName:

  44. 44 :param rowNo:

  45. 45 :param colNo:

  46. 46 :return: 字符串

  47. 47 '''

  48. 48 sh = self.wb[sheetName]

  49. 49 value = sh.cell(rowNo, colNo).value

  50. 50 return value

  51. 51 def writeCell(self, sheetName, rowNo, colNo, value):

  52. 52 '''

  53. 53 向某个单元格写入数据

  54. 54 :param rowNo: 行号

  55. 55 :param colNo: 列号

  56. 56 :param value:

  57. 57 :return: 无

  58. 58 '''

  59. 59 sh = self.wb[sheetName]

  60. 60 sh.cell(rowNo, colNo).value = value

  61. 61 self.wb.save(excelPath)

  62. 62 def writeCurrentTime(self, sheetName, rowNo, colNo):

  63. 63 '''

  64. 64 向某个单元格写入当前时间

  65. 65 :return:

  66. 66 '''

  67. 67 sh = self.wb[sheetName]

  68. 68 Time = datetime.now()

  69. 69 currentTime = Time.strftime('%Y:%m:%d %H:%M:%S')

  70. 70 sh.cell(rowNo, colNo).value = currentTime

  71. 71 self.wb.save(excelPath)

  72. 72

  73. 73 def writeTestResult(self, sheetName, rowNo, result, errorInfo = None, errorPic = None):

  74. 74 ParseExcel().writeCurrentTime(sheetName, rowNo, testStep_testRunTime)

  75. 75 ParseExcel().writeCell(sheetName, rowNo, testStep_testResult, result)

  76. 76 if errorInfo and errorInfo:

  77. 77 ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorInfo, errorInfo)

  78. 78 ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorPic, errorPic)

  79. 79 else:

  80. 80 ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorInfo, '')

  81. 81 ParseExcel().writeCell(sheetName, rowNo, testStep_testErrorPic, '')

  82. 82 if __name__=='__main__':

  83. 83 p = ParseExcel()

  84. 84 print(p.getRowValue('126account',2))

  85. 85 print(p.getColumnValue('126account',3))

  86. 86 print(p.getCellOfValue('126account', 2, 3))

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

 
  1. 1 import logging

  2. 2 import time

  3. 3 from config.VarConfig import *

  4. 4

  5. 5 class Logger(object):

  6. 6 '''

  7. 7 封装的日志模块

  8. 8 '''

  9. 9 def __init__(self, logger, CmdLevel=logging.INFO, FileLevel=logging.INFO):

  10. 10 """

  11. 11

  12. 12 :param logger:

  13. 13 :param CmdLevel:

  14. 14 :param FileLevel:

  15. 15 """

  16. 16 try:

  17. 17 self.logger = logging.getLogger(logger)

  18. 18 self.logger.setLevel(logging.DEBUG) # 设置日志输出的默认级别

  19. 19 # 日志输出格式

  20. 20 fmt = logging.Formatter('%(asctime)s - %(filename)s:[%(lineno)s] - [%(levelname)s] - %(message)s')

  21. 21 # 日志文件名称

  22. 22 # self.LogFileName = os.path.join(conf.log_path, "{0}.log.txt".format(time.strftime("%Y-%m-%d")))# %H_%M_%S

  23. 23 currTime = time.strftime("%Y-%m-%d")

  24. 24 self.LogFileName = logPath+currTime+'.txt'

  25. 25 # 设置控制台输出

  26. 26 # sh = logging.StreamHandler()

  27. 27 # sh.setFormatter(fmt)

  28. 28 # sh.setLevel(CmdLevel)# 日志级别

  29. 29

  30. 30 # 设置文件输出

  31. 31 fh = logging.FileHandler(self.LogFileName)

  32. 32 fh.setFormatter(fmt)

  33. 33 fh.setLevel(FileLevel)# 日志级别

  34. 34

  35. 35 # self.logger.addHandler(sh)

  36. 36 self.logger.addHandler(fh)

  37. 37 except Exception as e:

  38. 38 raise e

  39. 39

  40. 40 if __name__ == '__main__':

  41. 41 logger = Logger("fox",CmdLevel=logging.DEBUG, FileLevel=logging.DEBUG)

  42. 42 logger.logger.debug("debug")

  43. 43 logger.logger.log(logging.ERROR,'%(module)s %(info)s',{'module':'log日志','info':'error'}) #ERROR,log日志 error

3.2业务操作功能模块

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

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

 
  1. 1 from util.ObjectMap import *

  2. 2 from util.ClipboardUtil import Clipboard

  3. 3 from util.KeyBoardUtil import KeyBoardKeys

  4. 4 from util.WaitUntil import WaitUnit

  5. 5 from util.DirAndTime import *

  6. 6 from selenium import webdriver

  7. 7

  8. 8 driver = None

  9. 9 waitUtil = None

  10. 10 # 打开浏览器

  11. 11 def openBrowser(browser):

  12. 12 global driver, waitUtil

  13. 13 try:

  14. 14 if browser.lower() =='ie':

  15. 15 driver = webdriver.Ie(executable_path=iePath)

  16. 16 elif browser.lower() == 'chrome':

  17. 17 driver = webdriver.Chrome(executable_path=chromePath)

  18. 18 else:

  19. 19 # driver = webdriver.Firefox(executable_path=fireFox)

  20. 20 driver = webdriver.Firefox()

  21. 21 except Exception as e:

  22. 22 raise e

  23. 23 else:

  24. 24 waitUtil = WaitUnit(driver) # driver 创建之后, 创建等待类实例对象

  25. 25

  26. 26 # 浏览器窗口最大化

  27. 27 def maximize_browser():

  28. 28 try:

  29. 29 driver.maximize_window()

  30. 30 except Exception as e:

  31. 31 raise e

  32. 32 # 加载网址

  33. 33 def loadUrl(url):

  34. 34 try:

  35. 35 driver.get(url)

  36. 36 except Exception as e:

  37. 37 raise e

  38. 38

  39. 39 # 强制等待

  40. 40 def sleep(sleepSeconds):

  41. 41 try:

  42. 42 import time

  43. 43 time.sleep(sleepSeconds)

  44. 44 except Exception as e:

  45. 45 raise e

  46. 46 # 清除输入框的内容

  47. 47 def clear(by, locator):

  48. 48 try:

  49. 49 getElement(driver, by, locator).clear()

  50. 50 except Exception as e:

  51. 51 raise e

  52. 52 # 输入框中输入内容

  53. 53 def inputValue(by, locator, value):

  54. 54 try:

  55. 55 element = getElement(driver, by, locator)

  56. 56 # element.click()

  57. 57 element.send_keys(value)

  58. 58 except Exception as e:

  59. 59 raise e

  60. 60 # 点击操作

  61. 61 def clickBtn(by, locator):

  62. 62 try:

  63. 63 getElement(driver, by, locator).click()

  64. 64 except Exception as e:

  65. 65 raise e

  66. 66 # 断言页面的title

  67. 67 def assertTitle(titleStr):

  68. 68 try:

  69. 69 assert titleStr in driver.title, "%s not found in title!" % titleStr

  70. 70 except AssertionError as e:

  71. 71 raise AssertionError(e)

  72. 72 except Exception as e:

  73. 73 raise e

  74. 74

  75. 75 # 断言目标字符串是否包含在页面源码中

  76. 76 def assert_string_in_page_source(assertString):

  77. 77 try:

  78. 78 assert assertString in driver.page_source, "%s not found in page source!" % assertString

  79. 79 except AssertionError as e:

  80. 80 raise AssertionError(e)

  81. 81 except Exception as e:

  82. 82 raise e

  83. 83

  84. 84 # 获取当前页面的title

  85. 85 def getTitle():

  86. 86 try:

  87. 87 return driver.title

  88. 88 except Exception as e:

  89. 89 raise e

  90. 90

  91. 91 # 获取页面源码

  92. 92 def getPageSource():

  93. 93 try:

  94. 94 return driver.page_source

  95. 95 except Exception as e:

  96. 96 raise e

  97. 97 # 切换到frame里面

  98. 98 def switchToFrame(by, locator):

  99. 99 try:

  100. 100 driver.switch_to.frame(getElement(driver, by, locator))

  101. 101 except Exception as e:

  102. 102 raise e

  103. 103

  104. 104 # 跳到默认的frame

  105. 105 def switchToDefault():

  106. 106 try:

  107. 107 driver.switch_to.default_content()

  108. 108 except Exception as e:

  109. 109 raise e

  110. 110

  111. 111 # 模拟ctrl+v键

  112. 112 def ctrlV(value):

  113. 113 try:

  114. 114 Clipboard.setText(value)

  115. 115 sleep(2)

  116. 116 KeyBoardKeys.twoKeys('ctrl', 'v')

  117. 117 except Exception as e:

  118. 118 raise e

  119. 119

  120. 120 # 模拟tab键

  121. 121 def tabKey():

  122. 122 try:

  123. 123 KeyBoardKeys.oneKey('tab')

  124. 124 except Exception as e:

  125. 125 raise e

  126. 126

  127. 127 # 模拟enter键

  128. 128 def enterKey():

  129. 129 try:

  130. 130 KeyBoardKeys.oneKey('enter')

  131. 131 except Exception as e:

  132. 132 raise e

  133. 133

  134. 134 # 屏幕截图

  135. 135 def saveScreenShot():

  136. 136 pictureName = DirAndTime.CreatePicturePath() +'\\'+DirAndTime.getCurrentTime() + '.png'

  137. 137 try:

  138. 138 driver.get_screenshot_as_file(pictureName)

  139. 139 except Exception as e:

  140. 140 raise e

  141. 141 else:

  142. 142 return pictureName

  143. 143

  144. 144 def waitPresenceOfElementLocated(by, locator):

  145. 145 '''

  146. 146 显示等待页面元素出现在DOM中,单并不一定可见

  147. 147 :param by:

  148. 148 :param locator:

  149. 149 :return:

  150. 150 '''

  151. 151 waitUtil.presenceOfElementLocated(by, locator)

  152. 152

  153. 153 def waitFrameToBeAvailableAndSwitchToIt(by, locator):

  154. 154 '''

  155. 155 检查frame是否存在,存在就切换到frame中

  156. 156 :param by:

  157. 157 :param locator:

  158. 158 :return:

  159. 159 '''

  160. 160 waitUtil.frameToBeAvailableAndSwtichToIt(by, locator)

  161. 161

  162. 162 def waitVisibiltyOfElementLocated(by, locator):

  163. 163 '''

  164. 164 显示等待页面元素出现在DOM中,并且可见

  165. 165 :param by:

  166. 166 :param locator:

  167. 167 :return:

  168. 168 '''

  169. 169 waitUtil.visibiltyOfElementLocated(by, locator)

  170. 170

  171. 171 # 关闭浏览器

  172. 172 def quitBroswer():

  173. 173 try:

  174. 174 driver.quit()

  175. 175 except Exception as e:

  176. 176 raise e

  177. 177 if __name__=='__main__':

  178. 178 openBrowser('firefox')

  179. 179 loadUrl('http://www.baidu.com')

  180. 180 # inputValue('id', 'kw','python')

  181. 181 # clear('id', 'kw')

  182. 182 # inputValue('id', 'kw', 'python')

  183. 183 # clickBtn('id', 'su')

  184. 184 # sleep(3)

  185. 185 # title = getTitle()

  186. 186 # print(title)

  187. 187 # assertTitle('python')

  188. 188 # assert_string_in_page_source('python')

  189. 189 ctrlV('python')

四、项目数据文件设计

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

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

测试用例页

登录页

发送邮件页

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

五、项目配置模块

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

 
  1. 1 # 存储全局的变量

  2. 2 import os

  3. 3

  4. 4 # 项目根目录

  5. 5 projectPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

  6. 6 # 截图目录

  7. 7 exceptionPath = projectPath +r'\exceptionpictures'

  8. 8

  9. 9 # 驱动存放路径, 需要自己根据自己电脑的驱动为止修改

  10. 10 iePath = ''

  11. 11 chromePath = ''

  12. 12 fireFox = ''

  13. 13

  14. 14 # excel文件存放路径

  15. 15 excelPath = projectPath + r'\testData\126mailSend.xlsx'

  16. 16 # loh文件存放路径

  17. 17 logPath = projectPath + '\\log\\'

  18. 18 # 测试用例部分列对应的列号

  19. 19 testCase_testCaseName = 2

  20. 20 testCase_testStepName = 4

  21. 21 testCase_testIsExecute = 5

  22. 22 testCase_testRunEndTime = 6

  23. 23 testCase_testResult = 7

  24. 24

  25. 25 # 用例步骤对应的列号

  26. 26 testStep_testNum = 1

  27. 27 testStep_testStepDescribe = 2

  28. 28 testStep_keyWord = 3

  29. 29 testStep_elementBy = 4

  30. 30 testStep_elementLocator = 5

  31. 31 testStep_operateValue = 6

  32. 32 testStep_testRunTime = 7

  33. 33 testStep_testResult = 8

  34. 34 testStep_testErrorInfo = 9

  35. 35 testStep_testErrorPic = 10

  36. 36

  37. 37

  38. 38 if __name__=='__main__':

  39. 39

  40. 40 print(projectPath)

  41. 41 print(exceptionPath)

六、测试用例编写

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

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

 
  1. 1 from util.ParseExcel import ParseExcel

  2. 2 from config.VarConfig import *

  3. 3 from action.PageAction import *

  4. 4 import traceback

  5. 5 from util.log import Logger

  6. 6 import logging

  7. 7

  8. 8 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO)

  9. 9 p = ParseExcel()

  10. 10 sheetName = p.wb.sheetnames# 获取到excel的所有sheet名称

  11. 11

  12. 12 def Test126MailSendWithAtt():

  13. 13 try:

  14. 14 testCasePassNum = 0

  15. 15

  16. 16 requiredCase = 0

  17. 17 isExecuteColumnValues = p.getColumnValue(sheetName[0], testCase_testIsExecute)

  18. 18 # print(columnValues)

  19. 19 for index, value in enumerate(isExecuteColumnValues):

  20. 20 # print(index, value)

  21. 21 # 获取对应的步骤sheet名称

  22. 22 stepSheetName = p.getCellOfValue(sheetName[0],index+2, testCase_testStepName)

  23. 23 # print(stepSheetName)

  24. 24 if value.strip().lower() == 'y':

  25. 25 requiredCase += 1

  26. 26 testStepPassNum = 0

  27. 27 print('开始执行测试用例"{}"'.format(stepSheetName))

  28. 28 log.logger.info('开始执行测试用例"{}"'.format(stepSheetName))

  29. 29 # 如果用例被标记为执行y,切换到对应的sheet页

  30. 30 # 获取对应的sheet表中的总步骤数,关键字,定位方式,定位表达式,操作值

  31. 31 # 步骤总数

  32. 32 values = p.getColumnValue(stepSheetName, testStep_testNum) # 第一列数据

  33. 33 stepNum = len(values)

  34. 34 print(stepNum)

  35. 35 for step in range(2, stepNum+2):

  36. 36 rawValue = p.getRowValue(stepSheetName, step)

  37. 37 # 执行步骤名称

  38. 38 stepName = rawValue[testStep_testStepDescribe -2]

  39. 39 # 关键字

  40. 40 keyWord = rawValue[testStep_keyWord - 2]

  41. 41 # 定位方式

  42. 42 by = rawValue[testStep_elementBy - 2]

  43. 43 # 定位表达式

  44. 44 locator = rawValue[testStep_elementLocator - 2]

  45. 45 # 操作值

  46. 46 operateValue = rawValue[testStep_operateValue - 2]

  47. 47

  48. 48 if keyWord and by and locator and operateValue:

  49. 49 func = keyWord + '(' +'"' +by +'"'+ ',' +'"'+locator+ '"'+',' +'"'+ operateValue + '"'+')'

  50. 50 elif keyWord and by and locator and operateValue is None:

  51. 51 func = keyWord + '(' +'"' +by +'"'+ ',' +'"'+locator+ '"'+')'

  52. 52

  53. 53 elif keyWord and operateValue and type(operateValue) == str and by is None and locator is None:

  54. 54 func = keyWord + '(' +'"' + operateValue + '"' + ')'

  55. 55

  56. 56 elif keyWord and operateValue and type(operateValue) == int and by is None and locator is None:

  57. 57 func = keyWord + '(' + str(operateValue) +')'

  58. 58

  59. 59 else:

  60. 60 func = keyWord + '('+')'

  61. 61

  62. 62 try:

  63. 63 # 执行测试步骤

  64. 64 eval(func)

  65. 65 except Exception:

  66. 66 # 截图

  67. 67 picPath = saveScreenShot()

  68. 68 # 写回测试结果

  69. 69 errorInfo = traceback.format_exc()

  70. 70 p.writeTestResult(stepSheetName, step, 'Failed', errorInfo, picPath)

  71. 71 print('步骤"{}"执行失败'.format(stepName))

  72. 72 log.logger.info('步骤"{}"执行失败'.format(stepName))

  73. 73 raise

  74. 74 else:

  75. 75 print('步骤"{}"执行通过'.format(stepName))

  76. 76 log.logger.info('步骤"{}"执行通过'.format(stepName))

  77. 77 # 标记测试步骤为pass

  78. 78 p.writeTestResult(stepSheetName, step, 'Pass')

  79. 79 testStepPassNum += 1

  80. 80 # print('通过用例步数数:',testStepPassNum)

  81. 81 if testStepPassNum == stepNum:

  82. 82 # 标记测试用例sheet页的执行结果为pass

  83. 83 p.writeCell(sheetName[0], index+2, testCase_testResult, 'Pass')

  84. 84 testCasePassNum += 1

  85. 85 else:

  86. 86 p.writeCell(sheetName[0], index + 2, testCase_testResult, 'Failed')

  87. 87 print('共{}条用例,{}条需要被执行,本次执行通过{}条'.format(len(isExecuteColumnValues), requiredCase, testCasePassNum))

  88. 88 log.logger.info('共{}条用例,{}条需要被执行,本次执行通过{}条'.format(len(isExecuteColumnValues), requiredCase, testCasePassNum))

  89. 89 except Exception as e:

  90. 90 print(traceback.format_exc(e))

  91. 91 log.logger.info(traceback.format_exc(e))

  92. 92

  93. 93 if __name__=='__main__':

  94. 94 Test126MailSendWithAtt()

七、加载用例

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

 
  1. 1 if __name__=='__main__':

  2. 2 from testCases.Test126SendMailWithAttachment import Test126MailSendWithAtt

  3. 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. 加入我的软件测试交流群:680748947免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)

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

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

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关键字驱动自动化测试是一种测试方法,它将测试逻辑按照关键字进行分解,并形成一个数据文件,其中关键字对应封装的业务逻辑。关键字驱动的主要思想是将脚本与数据、界面元素名与测试内部对象名、测试描述与具体实现细节分离。\[2\] 在关键字驱动自动化测试中,测试脚本与业务和数据分离,这样可以节省大量对脚本的维护工作。测试脚本描述了一个测试事例应该做什么,而不是如何做。测试脚本通过调用测试用例来具体执行测试业务逻辑。\[3\] 关键字驱动自动化测试框架具有以下优势:首先,它可以根据界面的变化更新对应的关键字对象,而不需要重新录制脚本,因此在受界面影响方面具有明显的优势。其次,关键字驱动自动化测试框架可以提高自动化测试脚本的维护效率,因为脚本与业务和数据分离,减少了对脚本的维护工作量。\[1\] 总之,关键字驱动自动化测试是一种将测试逻辑按照关键字进行分解的测试方法,它可以提高自动化测试脚本的维护效率,并且可以根据界面的变化更新对应的关键字对象。 #### 引用[.reference_title] - *1* *2* *3* [什么是关键字驱动自动化测试](https://blog.csdn.net/A_Kaka/article/details/107519421)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值