Pytest UI自动化测试实战实例

环境准备

序号库/插件/工具安装命令
1确保您已经安装了python3.x
2配置python3+pycharm+selenium2开发环境
3安装pytest库pip install pytest
4安装pytest -html 报告插件pip install pytest-html
5安装pypiwin32库(用来模拟按键)pip install pypiwin32
6安装openpyxl解析excel文件库pip install openpyxl
7安装yagmail发送报告库pip install yagmail
8确保已配置火狐或谷歌浏览器及对应驱动
9确保已经正确配置好发送邮件的邮箱

项目简介

测试地址

https://mail.126.com

测试范围

1.126电子邮箱登录功能测试-验证正确帐号密码登录成功-验证错误用户名密码登录失败(有很多情况,用例里面做了充分的校验)

2.126电子邮箱添加联系人功能测试-验证正确填写必填项数据添加联系人成功-验证缺省必填项数据添加联系人失败-验证必填项字段数据格式错误添加联系人失败

3.126电子邮箱发送邮件功能测试-验证普通邮件发送成功-验证带附件邮件发送成功

项目设计

1.python编程语言设计测试脚本

2.webdriver驱动浏览器并操作页面元素

3.二次封装webdriver Api 操作方法

4.采用PageObject设计模式,设计测试业务流程

5.通过UI对象库存储页面操作元素

6.通过数据文件存储数据,读取数据,参数化测试用例并驱动测试执行

7.通过第三方插件pytest-html生成测试报告

8.通过yagmail第三方库,编写发送报告接口,测试工作完成后自动发送测试报告

代码分析目录结构
 
  1. 1 PytestAutoTestFrameWork

  2. 2 |—|config

  3. 3 |——|__init__.py

  4. 4 |——|conf.py

  5. 5 |——|config.ini

  6. 6 |—|data

  7. 7 |——|__init__.py

  8. 8 |——|tcData.xlsx

  9. 9 |—Page

  10. 10 |——|PageObject.py

  11. 11 |———|__init__.py

  12. 12 |———|ContactPage.py

  13. 13 |———|HomePage.py

  14. 14 |———|LoginPage.py

  15. 15 |———|SendMailPage.py

  16. 16 |——|__init__.py

  17. 17 |——|BasePage.py

  18. 18 |—|report

  19. 19 |—|TestCases

  20. 20 |——|__init__.py

  21. 21 |——|conftest.py

  22. 22 |——|test_confactCase.py

  23. 23 |——|test_loginCase.py

  24. 24 |——|test_sendMailCase.py

  25. 25 |—|util

  26. 26 |——|__init__.py

  27. 27 |——|clipboard.py

  28. 28 |——|keyboard.py

  29. 29 |——|parseConFile.py

  30. 30 |——|parseExcelFile.py

  31. 31 |——|sendMailForReport.py

  32. 32 |—|conftest.py

  33. 33 |—|pytest.ini

  34. 34 |—|RunTestCase.py

代码实现

通过126邮箱测试范围分析,我们需要通过设计剪切板,模拟键盘完成附件上传操作,因此我们首先来编写这两个方法

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/15 12:04

  4. 4 @Auth : linux超

  5. 5 @File : clipboard.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import win32con

  11. 11 import win32clipboard as WC

  12. 12

  13. 13

  14. 14 class ClipBoard(object):

  15. 15 '''设置剪切板内容和获取剪切板内容'''

  16. 16

  17. 17 @staticmethod

  18. 18 def getText():

  19. 19 '''获取剪切板的内容'''

  20. 20 WC.OpenClipboard()

  21. 21 value = WC.GetClipboardData(win32con.CF_TEXT)

  22. 22 WC.CloseClipboard()

  23. 23 return value

  24. 24

  25. 25 @staticmethod

  26. 26 def setText(value):

  27. 27 '''设置剪切板的内容'''

  28. 28 WC.OpenClipboard()

  29. 29 WC.EmptyClipboard()

  30. 30 WC.SetClipboardData(win32con.CF_UNICODETEXT, value)

  31. 31 WC.CloseClipboard()

  32. 32

  33. 33

  34. 34 if __name__ == '__main__':

  35. 35 from selenium import webdriver

  36. 36

  37. 37 value = 'python'

  38. 38 driver = webdriver.Firefox()

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

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

  41. 41 ClipBoard.setText(value)

  42. 42 clValue = ClipBoard.getText()

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

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/15 12:05

  4. 4 @Auth : linux超

  5. 5 @File : keyboard.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10

  11. 11 # 模拟按键

  12. 12 import win32api

  13. 13 import win32con

  14. 14 import time

  15. 15

  16. 16

  17. 17 class KeyBoard(object):

  18. 18 """模拟按键"""

  19. 19 # 键盘码

  20. 20 vk_code = {

  21. 21 'enter' : 0x0D,

  22. 22 'tab' : 0x09,

  23. 23 'ctrl' : 0x11,

  24. 24 'v' : 0x56,

  25. 25 'a' : 0x41,

  26. 26 'x' : 0x58

  27. 27 }

  28. 28

  29. 29 @staticmethod

  30. 30 def keyDown(key_name):

  31. 31 """按下键"""

  32. 32 key_name = key_name.lower()

  33. 33 try:

  34. 34 win32api.keybd_event(KeyBoard.vk_code[key_name], 0, 0, 0)

  35. 35 except Exception as e:

  36. 36 print('未按下enter键')

  37. 37 print(e)

  38. 38

  39. 39 @staticmethod

  40. 40 def keyUp(key_name):

  41. 41 """抬起键"""

  42. 42 key_name = key_name.lower()

  43. 43 win32api.keybd_event(KeyBoard.vk_code[key_name], 0, win32con.KEYEVENTF_KEYUP, 0)

  44. 44

  45. 45 @staticmethod

  46. 46 def oneKey(key):

  47. 47 """模拟单个按键"""

  48. 48 key = key.lower()

  49. 49 KeyBoard.keyDown(key)

  50. 50 time.sleep(2)

  51. 51 KeyBoard.keyUp(key)

  52. 52

  53. 53 @staticmethod

  54. 54 def twoKeys(key1, key2):

  55. 55 """模拟组合按键"""

  56. 56 key1 = key1.lower()

  57. 57 key2 = key2.lower()

  58. 58 KeyBoard.keyDown(key1)

  59. 59 KeyBoard.keyDown(key2)

  60. 60 KeyBoard.keyUp(key1)

  61. 61 KeyBoard.keyUp(key2)

  62. 62

  63. 63

  64. 64 if __name__ == '__main__':

  65. 65 from selenium import webdriver

  66. 66 driver = webdriver.Firefox()

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

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

  69. 69 KeyBoard.twoKeys('ctrl', 'a')

  70. 70 KeyBoard.twoKeys('ctrl', 'x')

通过测试项目设计,我们需要把测试数据存放在Excel文件中,把页面操作元素存在UI对象库中也就是一个配置文件,那么我们需要对Excel 和 ini文件解析,因此我们开始编写这两个方法,设计UI对象库和测试数据文件

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/22 16:12

  4. 4 @Auth : linux超

  5. 5 @File : parseExcelFile.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from openpyxl import load_workbook

  11. 11 from config.conf import excelPath

  12. 12

  13. 13

  14. 14 class ParseExcel(object):

  15. 15

  16. 16 def __init__(self):

  17. 17 self.wk = load_workbook(excelPath)

  18. 18 self.excelFile = excelPath

  19. 19

  20. 20 def getSheetByName(self, sheetName):

  21. 21 """获取sheet对象"""

  22. 22 sheet = self.wk[sheetName]

  23. 23 return sheet

  24. 24

  25. 25 def getRowNum(self, sheet):

  26. 26 """获取有效数据的最大行号"""

  27. 27 return sheet.max_row

  28. 28

  29. 29 def getColsNum(self, sheet):

  30. 30 """获取有效数据的最大列号"""

  31. 31 return sheet.max_column

  32. 32

  33. 33 def getRowValues(self, sheet, rowNum):

  34. 34 """获取某一行的数据"""

  35. 35 maxColsNum = self.getColsNum(sheet)

  36. 36 rowValues = []

  37. 37 for colsNum in range(1, maxColsNum + 1):

  38. 38 value = sheet.cell(rowNum, colsNum).value

  39. 39 if value is None:

  40. 40 value = ''

  41. 41 rowValues.append(value)

  42. 42 return tuple(rowValues)

  43. 43

  44. 44 def getColumnValues(self, sheet, columnNum):

  45. 45 """获取某一列的数据"""

  46. 46 maxRowNum = self.getRowNum(sheet)

  47. 47 columnValues = []

  48. 48 for rowNum in range(2, maxRowNum + 1):

  49. 49 value = sheet.cell(rowNum, columnNum).value

  50. 50 if value is None:

  51. 51 value = ''

  52. 52 columnValues.append(value)

  53. 53 return tuple(columnValues)

  54. 54

  55. 55 def getValueOfCell(self, sheet, rowNum, columnNum):

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

  57. 57 value = sheet.cell(rowNum, columnNum).value

  58. 58 if value is None:

  59. 59 value = ''

  60. 60 return value

  61. 61

  62. 62 def getAllValuesOfSheet(self, sheet):

  63. 63 """获取某一个sheet页的所有测试数据,返回一个元祖组成的列表"""

  64. 64 maxRowNum = self.getRowNum(sheet)

  65. 65 columnNum = self.getColsNum(sheet)

  66. 66 allValues = []

  67. 67 for row in range(2, maxRowNum + 1):

  68. 68 rowValues = []

  69. 69 for column in range(1, columnNum + 1):

  70. 70 value = sheet.cell(row, column).value

  71. 71 if value is None:

  72. 72 value = ''

  73. 73 rowValues.append(value)

  74. 74 allValues.append(tuple(rowValues))

  75. 75 return allValues

  76. 76

  77. 77

  78. 78 if __name__ == '__main__':

  79. 79 # excel = ParseExcel()

  80. 80 # sheet = excel.getSheetByName('login')

  81. 81 # print('行号:', excel.getRowNum(sheet))

  82. 82 # print('列号:', excel.getColsNum(sheet))

  83. 83 #

  84. 84 # rowvalues = excel.getRowValues(sheet, 1)

  85. 85 # columnvalues = excel.getColumnValues(sheet, 2)

  86. 86 # valueofcell = excel.getValueOfCell(sheet, 1, 2)

  87. 87 # allvalues = excel.getAllValuesOfSheet(sheet)

  88. 88 #

  89. 89 # print('第{}行数据{}'.format(1, rowvalues))

  90. 90 # print('第{}列数据{}'.format(2, columnvalues))

  91. 91 # print('{}{}单元格的内容{}'.format(1, 2, valueofcell))

  92. 92 # print('login{}'.format(allvalues))

  93. 93

  94. 94 excel = ParseExcel()

  95. 95 sheet = excel.getSheetByName('mail')

  96. 96 print('行号:', excel.getRowNum(sheet))

  97. 97 print('列号:', excel.getColsNum(sheet))

  98. 98

  99. 99 allvalues = excel.getAllValuesOfSheet(sheet)

  100. 100

  101. 101 print('sendmail{}'.format(allvalues))

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/18 10:54

  4. 4 @Auth : linux超

  5. 5 @File : parseConFile.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import configparser

  11. 11 from config.conf import configDir

  12. 12

  13. 13

  14. 14 class ParseConFile(object):

  15. 15

  16. 16 def __init__(self):

  17. 17 self.file = configDir

  18. 18 self.conf = configparser.ConfigParser()

  19. 19 self.conf.read(self.file, encoding='utf-8')

  20. 20

  21. 21 def getAllSections(self):

  22. 22 """获取所有的section,返回一个列表"""

  23. 23 return self.conf.sections()

  24. 24

  25. 25 def getAllOptions(self, section):

  26. 26 """获取指定section下所有的option, 返回列表"""

  27. 27 return self.conf.options(section)

  28. 28

  29. 29 def getLocatorsOrAccount(self, section, option):

  30. 30 """获取指定section, 指定option对应的数据, 返回元祖和字符串"""

  31. 31 try:

  32. 32 locator = self.conf.get(section, option)

  33. 33 if ('->' in locator):

  34. 34 locator = tuple(locator.split('->'))

  35. 35 return locator

  36. 36 except configparser.NoOptionError as e:

  37. 37 print('error:', e)

  38. 38 return 'error: No option "{}" in section: "{}"'.format(option, section)

  39. 39

  40. 40 def getOptionValue(self, section):

  41. 41 """获取指定section下所有的option和对应的数据,返回字典"""

  42. 42 value = dict(self.conf.items(section))

  43. 43 return value

  44. 44

  45. 45

  46. 46 if __name__ == '__main__':

  47. 47 cf = ParseConFile()

  48. 48 print(cf.getAllSections())

  49. 49 print(cf.getAllOptions('126LoginAccount'))

  50. 50 print(cf.getLocatorsOrAccount('126LoginAccount', 'username'))

  51. 51 print(cf.getOptionValue('126LoginAccount'))

 
  1. 1 [126LoginAccount];126邮箱正确的登录账号和密码;运行用例时请更换正确的用户名和密码

  2. 2 username=linuxxiaochao

  3. 3 password=xiaochao11520

  4. 4 [HomePageElements];126邮箱首页菜单栏元素

  5. 5 homePage=id->_mail_tabitem_0_3text

  6. 6 mailList=id->_mail_tabitem_1_4text

  7. 7 applicationCenter=id->_mail_tabitem_2_5text

  8. 8 inBox=id->_mail_tabitem_3_6text

  9. 9 [LoginPageElements];126邮箱登录页面的元素

  10. 10 frame=xpath->//div[@id="loginDiv"]/iframe

  11. 11 username=xpath->//input[@name="email"]

  12. 12 password=xpath->//input[@name="password"]

  13. 13 loginBtn=xpath->//a[@id="dologin"]

  14. 14 ferrorHead=xpath->//div[@class="ferrorhead"]

  15. 15 [ContactPageElements];126邮箱添加联系人页面元素

  16. 16 new_contact=xpath->//span[text()="新建联系人"]

  17. 17 name=id->input_N

  18. 18 mail=xpath->//div[@id="iaddress_MAIL_wrap"]//input[@class="nui-ipt-input"]

  19. 19 star=xpath->//span[@class="nui-chk-text"]/preceding-sibling::span/b

  20. 20 phone=xpath->//div[@id='iaddress_TEL_wrap']//input[@class='nui-ipt-input']

  21. 21 comment=id->input_DETAIL

  22. 22 commit=xpath->//span[text()='确 定']

  23. 23 tooltip=xpath->//span[text()='请正确填写邮件地址。']

  24. 24 [SendMailPageElements];126邮箱发送邮件页面元素

  25. 25 writeMail=xpath->//div[@id='dvNavContainer']//span[text()='写 信']

  26. 26 addressee=xpath->//input[@aria-label='收件人地址输入框,请输入邮件地址,多人时地址请以分号隔开']

  27. 27 subject=xpath->//input[contains(@id, '_subjectInput')]

  28. 28 iframe=xpath->//iframe[@class="APP-editor-iframe"]

  29. 29 text=xpath->/html/body

  30. 30 sendBtn=xpath->//header//span[text()='发送']

  31. 31 expect=xpath->//h1[contains(@id,'_succInfo')]

  32. 32 uploadAttachment=xpath->//div[@title="点击添加附件"]

  33. 33 delete=xpath->//a[text()='删除']

新建excel文件,分3个sheet,分别为:login,contact,mail #每个sheet中数据可自行填写,驱动测试用例执行不同的数据进行测试

login

contact

mail

数据,UI对象库,解析方法都已经有了,接下来通过PageObject模式设计编写每个页面的操作及封装126邮箱的功能,以便后续设计用例调用

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 8:45

  4. 4 @Auth : linux超

  5. 5 @File : BasePage.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import time

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

  12. 12 from selenium.webdriver.support.wait import WebDriverWait as wd

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

  14. 14 from selenium.common.exceptions import NoSuchWindowException, TimeoutException, \

  15. 15 NoAlertPresentException, NoSuchFrameException

  16. 16 from selenium import webdriver

  17. 17

  18. 18 from util.clipboard import ClipBoard

  19. 19 from util.keyboard import KeyBoard

  20. 20 from util.parseConFile import ParseConFile

  21. 21 from util.parseExcelFile import ParseExcel

  22. 22

  23. 23

  24. 24 class BasePage(object):

  25. 25 """

  26. 26 结合显示等待封装一些selenium 内置方法

  27. 27 """

  28. 28 cf = ParseConFile()

  29. 29 excel = ParseExcel()

  30. 30

  31. 31 def __init__(self, driver, outTime=30):

  32. 32 self.byDic = {

  33. 33 'id': By.ID,

  34. 34 'name': By.NAME,

  35. 35 'class_name': By.CLASS_NAME,

  36. 36 'xpath': By.XPATH,

  37. 37 'link_text': By.LINK_TEXT

  38. 38 }

  39. 39 self.driver = driver

  40. 40 self.outTime = outTime

  41. 41

  42. 42 def findElement(self, by, locator):

  43. 43 """

  44. 44 find alone element

  45. 45 :param by: eg: id, name, xpath, css.....

  46. 46 :param locator: id, name, xpath for str

  47. 47 :return: element object

  48. 48 """

  49. 49 try:

  50. 50 print('[Info:Starting find the element "{}" by "{}"!]'.format(locator, by))

  51. 51 element = wd(self.driver, self.outTime).until(lambda x : x.find_element(by, locator))

  52. 52 except TimeoutException as t:

  53. 53 print('error: found "{}" timeout!'.format(locator), t)

  54. 54 except NoSuchWindowException as e:

  55. 55 print('error: no such "{}"'.format(locator), e)

  56. 56 except Exception as e:

  57. 57 raise e

  58. 58 else:

  59. 59 # print('[Info:Had found the element "{}" by "{}"!]'.format(locator, by))

  60. 60 return element

  61. 61

  62. 62 def findElements(self, by, locator):

  63. 63 """

  64. 64 find group elements

  65. 65 :param by: eg: id, name, xpath, css.....

  66. 66 :param locator: eg: id, name, xpath for str

  67. 67 :return: elements object

  68. 68 """

  69. 69 try:

  70. 70 print('[Info:start find the elements "{}" by "{}"!]'.format(locator, by))

  71. 71 elements = wd(self.driver, self.outTime).until(lambda x : x.find_element(by, locator))

  72. 72 except TimeoutException as t:

  73. 73 print(t)

  74. 74 except NoSuchWindowException as e:

  75. 75 print(e)

  76. 76 except Exception as e:

  77. 77 raise e

  78. 78 else:

  79. 79 # print('[Info:Had found the elements "{}" by "{}"!]'.format(locator, by))

  80. 80 return elements

  81. 81

  82. 82 def isElementExsit(self, by, locator):

  83. 83 """

  84. 84 assert element if exist

  85. 85 :param by: eg: id, name, xpath, css.....

  86. 86 :param locator: eg: id, name, xpath for str

  87. 87 :return: if element return True else return false

  88. 88 """

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

  90. 90 try:

  91. 91 wd(self.driver, self.outTime).\

  92. 92 until(EC.visibility_of_element_located((self.byDic[by], locator)))

  93. 93 except TimeoutException:

  94. 94 print('Error: element "{}" time out!'.format(locator))

  95. 95 return False

  96. 96 except NoSuchWindowException:

  97. 97 print('Error: element "{}" not exsit!'.format(locator))

  98. 98 return False

  99. 99 return True

  100. 100 else:

  101. 101 print('the "{}" error!'.format(by))

  102. 102

  103. 103 def isClick(self, by, locator):

  104. 104 """判断是否可点击,返回元素对象"""

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

  106. 106 try:

  107. 107 element = wd(self.driver, self.outTime).\

  108. 108 until(EC.element_to_be_clickable((self.byDic[by], locator)))

  109. 109 except Exception:

  110. 110 return False

  111. 111 return element

  112. 112 else:

  113. 113 print('the "{}" error!'.format(by))

  114. 114

  115. 115 def isAlertAndSwitchToIt(self):

  116. 116 """

  117. 117 assert alert if exsit

  118. 118 :return: alert obj

  119. 119 """

  120. 120 try:

  121. 121 re = wd(self.driver, self.outTime).until(EC.alert_is_present())

  122. 122 except NoAlertPresentException:

  123. 123 return False

  124. 124 except Exception:

  125. 125 return False

  126. 126 return re

  127. 127

  128. 128 def switchToFrame(self, by, locator):

  129. 129 """判断frame是否存在,存在就跳到frame"""

  130. 130 print('info:switching to iframe "{}"'.format(locator))

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

  132. 132 try:

  133. 133 wd(self.driver, self.outTime).\

  134. 134 until(EC.frame_to_be_available_and_switch_to_it((self.byDic[by], locator)))

  135. 135 except TimeoutException as t:

  136. 136 print('error: found "{}" timeout!'.format(locator), t)

  137. 137 except NoSuchFrameException as e:

  138. 138 print('error: no such "{}"'.format(locator), e)

  139. 139 except Exception as e:

  140. 140 raise e

  141. 141 else:

  142. 142 print('the "{}" error!'.format(by))

  143. 143

  144. 144 def switchToDefaultFrame(self):

  145. 145 """返回默认的frame"""

  146. 146 print('info:switch back to default iframe')

  147. 147 try:

  148. 148 self.driver.switch_to.default_content()

  149. 149 except Exception as e:

  150. 150 print(e)

  151. 151

  152. 152 def getAlertText(self):

  153. 153 """获取alert的提示信息"""

  154. 154 if self.isAlertAndSwitchToIt():

  155. 155 alert = self.isAlertAndSwitchToIt()

  156. 156 return alert.text

  157. 157 else:

  158. 158 return None

  159. 159

  160. 160 def getElementText(self, by, locator, name=None):

  161. 161 """获取某一个元素的text信息"""

  162. 162 try:

  163. 163 element = self.findElement(by, locator)

  164. 164 if name:

  165. 165 return element.get_attribute(name)

  166. 166 else:

  167. 167 return element.text

  168. 168 except:

  169. 169 print('get "{}" text failed return None'.format(locator))

  170. 170 return None

  171. 171

  172. 172 def loadUrl(self, url):

  173. 173 """加载url"""

  174. 174 print('info: string upload url "{}"'.format(url))

  175. 175 self.driver.get(url)

  176. 176

  177. 177 def getSource(self):

  178. 178 """获取页面源码"""

  179. 179 return self.driver.page_source

  180. 180

  181. 181 def sendKeys(self, by, locator, value=''):

  182. 182 """写数据"""

  183. 183 print('info:input "{}"'.format(value))

  184. 184 try:

  185. 185 element = self.findElement(by, locator)

  186. 186 element.send_keys(value)

  187. 187 except AttributeError as e:

  188. 188 print(e)

  189. 189

  190. 190 def clear(self, by, locator):

  191. 191 """清理数据"""

  192. 192 print('info:clearing value')

  193. 193 try:

  194. 194 element = self.findElement(by, locator)

  195. 195 element.clear()

  196. 196 except AttributeError as e:

  197. 197 print(e)

  198. 198

  199. 199 def click(self, by, locator):

  200. 200 """点击某个元素"""

  201. 201 print('info:click "{}"'.format(locator))

  202. 202 element = self.isClick(by, locator)

  203. 203 if element:

  204. 204 element.click()

  205. 205 else:

  206. 206 print('the "{}" unclickable!')

  207. 207

  208. 208 def sleep(self, num=0):

  209. 209 """强制等待"""

  210. 210 print('info:sleep "{}" minutes'.format(num))

  211. 211 time.sleep(num)

  212. 212

  213. 213 def ctrlV(self, value):

  214. 214 """ctrl + V 粘贴"""

  215. 215 print('info:pasting "{}"'.format(value))

  216. 216 ClipBoard.setText(value)

  217. 217 self.sleep(3)

  218. 218 KeyBoard.twoKeys('ctrl', 'v')

  219. 219

  220. 220 def enterKey(self):

  221. 221 """enter 回车键"""

  222. 222 print('info:keydown enter')

  223. 223 KeyBoard.oneKey('enter')

  224. 224

  225. 225 def waitElementtobelocated(self, by, locator):

  226. 226 """显示等待某个元素出现,且可见"""

  227. 227 print('info:waiting "{}" to be located'.format(locator))

  228. 228 try:

  229. 229 wd(self.driver, self.outTime).until(EC.visibility_of_element_located((self.byDic[by], locator)))

  230. 230 except TimeoutException as t:

  231. 231 print('error: found "{}" timeout!'.format(locator), t)

  232. 232 except NoSuchWindowException as e:

  233. 233 print('error: no such "{}"'.format(locator), e)

  234. 234 except Exception as e:

  235. 235 raise e

  236. 236

  237. 237 def assertValueInSource(self, value):

  238. 238 """断言某个关键字是否存在页面源码中"""

  239. 239 print('info:assert "{}" in page source'.format(value))

  240. 240 source = self.getSource()

  241. 241 assert value in source, '关键字"{}"不存在源码中!'.format(value)

  242. 242

  243. 243 def assertStringContainsValue(self, String, value):

  244. 244 """断言某段字符串包含另一个字符串"""

  245. 245 print('info:assert "{}" contains "{}"'.format(String, value))

  246. 246 assert value in String, '"{}"不包含"{}"!'.format(String, value)

  247. 247

  248. 248

  249. 249 @staticmethod

  250. 250 def getSheet(sheetName):

  251. 251 """获取某个sheet页的对象"""

  252. 252 sheet = BasePage.excel.getSheetByName(sheetName)

  253. 253 return sheet

  254. 254

  255. 255

  256. 256 if __name__ == "__main__":

  257. 257 driver = webdriver.Firefox()

  258. 258 frame = ('xpath', '//div[@id="loginDiv"]/ifram')

  259. 259 wait = BasePage(driver)

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

  261. 261 wait.switchToFrame(*frame)

  262. 262 username = wait.findElement('xpath', '//input[@name="email"]')

  263. 263 username.send_keys('账号')

  264. 264 if wait.isElementExsit('xpath', '//input[@name="password"]'):

  265. 265 wait.findElement('xpath', '//input[@name="password"]').send_keys('xiaochao11520')

  266. 266 wait.click('xpath', '//a[@id="dologin"]')

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 12:28

  4. 4 @Auth : linux超

  5. 5 @File : HomePage.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from Page.BasePage import BasePage

  11. 11

  12. 12

  13. 13 class HomePage(BasePage):

  14. 14 # 配置文件读取元素

  15. 15 homePage = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'homePage')

  16. 16 mailList = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'mailList')

  17. 17 applicationCenter = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'applicationCenter')

  18. 18 inBox = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'inBox')

  19. 19 '''首页菜单选项'''

  20. 20 def selectMenu(self, Menu='mailList'):

  21. 21 """邮箱首页选择菜单"""

  22. 22 if Menu == 'mailList':

  23. 23 self.click(*HomePage.mailList)

  24. 24 elif Menu == 'homePage':

  25. 25 self.click(*HomePage.homePage)

  26. 26 elif Menu == 'applicationCenter':

  27. 27 self.click(*HomePage.applicationCenter)

  28. 28 elif Menu == 'inBox':

  29. 29 self.click(*HomePage.inBox)

  30. 30 else:

  31. 31 raise ValueError('''

  32. 32 菜单选择错误!

  33. 33 homePage->首页

  34. 34 mailList->通讯录

  35. 35 applicationCenter->应用中心

  36. 36 inBox->收件箱''')

  37. 37

  38. 38 if __name__=='__main__':

  39. 39 from selenium import webdriver

  40. 40 from Page.PageObject.LoginPage import LoginPage

  41. 41 driver = webdriver.Firefox()

  42. 42 login = LoginPage(driver)

  43. 43 login.login('账号', 'xiaochao11520')

  44. 44

  45. 45 home = HomePage(driver)

  46. 46 home.selectMenu()

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 12:28

  4. 4 @Auth : linux超

  5. 5 @File : LoginPage.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from Page.BasePage import BasePage

  11. 11

  12. 12

  13. 13 class LoginPage(BasePage):

  14. 14

  15. 15 # 配置文件读取元素

  16. 16 frame = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'frame')

  17. 17 username = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'username')

  18. 18 password = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'password')

  19. 19 loginBtn = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'loginBtn')

  20. 20 ferrorHead = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'ferrorHead') # 登录失败提示

  21. 21

  22. 22 def login(self, userName, passWord):

  23. 23 '''登录'''

  24. 24 print('-------staring login-------')

  25. 25 self.loadUrl('https://mail.126.com')

  26. 26 self.switchToFrame(*LoginPage.frame)

  27. 27 self.clear(*LoginPage.username)

  28. 28 self.sendKeys(*LoginPage.username, userName)

  29. 29 self.clear(*LoginPage.password)

  30. 30 self.sendKeys(*LoginPage.password, passWord)

  31. 31 self.click(*LoginPage.loginBtn)

  32. 32 self.switchToDefaultFrame()

  33. 33 print('---------end login---------')

  34. 34

  35. 35 # add at 2019/04/19

  36. 36 def assertTextEqString(self, expected, name = None):

  37. 37 '''断言提示信息是否与期望的值相等'''

  38. 38 self.switchToFrame(*LoginPage.frame)

  39. 39 text = self.getElementText(*LoginPage.ferrorHead, name)

  40. 40 self.switchToDefaultFrame()

  41. 41 print('info: assert "{}" == "{}"'.format(text, expected))

  42. 42 assert text == expected, '{} != {}'.format(text, expected)

  43. 43

  44. 44 if __name__=="__main__":

  45. 45 from selenium import webdriver

  46. 46 driver = webdriver.Firefox()

  47. 47 login = LoginPage(driver, 30)

  48. 48 login.login('lin', '')

  49. 49 login.assertTextEqString('请输入密码')

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 12:29

  4. 4 @Auth : linux超

  5. 5 @File : ContactPage.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from Page.BasePage import BasePage

  11. 11

  12. 12

  13. 13 class ContactPage(BasePage):

  14. 14 # 配置文件读取元素

  15. 15 new_contact = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'new_contact')

  16. 16 name = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'name')

  17. 17 mail = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'mail')

  18. 18 star = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'star')

  19. 19 phone = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'phone')

  20. 20 comment = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'comment')

  21. 21 commit = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'commit')

  22. 22 errortip = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'tooltip') # 错误提示

  23. 23

  24. 24 def newContact(self, Name, Mail, Star, Phone, Comment):

  25. 25 """添加联系人"""

  26. 26 print('--------string add contact--------')

  27. 27 self.click(*ContactPage.new_contact)

  28. 28 self.sendKeys(*ContactPage.name, Name)

  29. 29 self.sendKeys(*ContactPage.mail, Mail)

  30. 30 if Star == '1':

  31. 31 self.click(*ContactPage.star)

  32. 32 self.sendKeys(*ContactPage.phone, Phone)

  33. 33 self.sendKeys(*ContactPage.comment, Comment)

  34. 34 self.click(*ContactPage.commit)

  35. 35 print('--------end add contact--------')

  36. 36

  37. 37 def assertErrorTip(self, excepted):

  38. 38 """断言联系人添加失败时是否有提示信息"""

  39. 39 text = self.getElementText(*ContactPage.errortip)

  40. 40 print('info: assert "{}"=="{}"'.format(text, excepted))

  41. 41 assert text == excepted

  42. 42

  43. 43 if __name__ == '__main__':

  44. 44 from selenium import webdriver

  45. 45 from Page.PageObject.LoginPage import LoginPage

  46. 46 from Page.PageObject.HomePage import HomePage

  47. 47 driver = webdriver.Firefox()

  48. 48 home = HomePage(driver)

  49. 49 login = LoginPage(driver)

  50. 50 contact = ContactPage(driver)

  51. 51

  52. 52 login.login('账号', 'xiaochao11520')

  53. 53 home.selectMenu()

  54. 54 contact.newContact('281754041@qq.com')

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 9:16

  4. 4 @Auth : linux超

  5. 5 @File : SendMailPage.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from Page.BasePage import BasePage

  11. 11

  12. 12

  13. 13 class SendMailPage(BasePage):

  14. 14 # 配置文件读取元素

  15. 15 writeMail = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'writeMail')

  16. 16 addressee = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'addressee')

  17. 17 subject = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'subject')

  18. 18 iframe = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'iframe')

  19. 19 text = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'text')

  20. 20 sendBtn = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'sendBtn')

  21. 21 expect = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'expect')

  22. 22 uploadAttachment = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'uploadAttachment')

  23. 23 delete = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'delete')

  24. 24

  25. 25 def sendMail(self, Address, Subject, Text, PFA=''):

  26. 26 """发送邮件功能"""

  27. 27 print('------------string send mail---------------------')

  28. 28 self.click(*SendMailPage.writeMail)

  29. 29 self.sendKeys(*SendMailPage.addressee, Address)

  30. 30 self.sendKeys(*SendMailPage.subject, Subject)

  31. 31 self.switchToFrame(*SendMailPage.iframe)

  32. 32 self.sendKeys(*SendMailPage.text, Text)

  33. 33 self.switchToDefaultFrame()

  34. 34 if PFA:

  35. 35 self.click(*SendMailPage.uploadAttachment)

  36. 36 self.ctrlV(PFA)

  37. 37 self.enterKey()

  38. 38 self.waitElementtobelocated(*SendMailPage.delete)

  39. 39 self.click(*SendMailPage.sendBtn)

  40. 40 print('------------end send mail---------------------')

  41. 41

  42. 42 if __name__=='__main__':

  43. 43 from Page.PageObject.LoginPage import LoginPage

  44. 44 from selenium import webdriver

  45. 45 driver = webdriver.Firefox()

  46. 46

  47. 47 login = LoginPage(driver)

  48. 48 login.login('账号', 'xiaochao11520')

  49. 49 sendMail = SendMailPage(driver)

  50. 50 sendMail.sendMail('281754043@qq.com', 'pytest', 'pytest实战实例', 1, 'D:\KeyWordDriverTestFrameWork\geckodriver.log')

所有的准备工作都已经做好了,还有一个问题,我们的添加联系人和发送邮件应该是否应该在已经登录的前提下测试呢?答案是肯定的。所以我们在用例同目录下新建conftest.py文件并调用登录功能

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 15:10

  4. 4 @Auth : linux超

  5. 5 @File : conftest.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import pytest

  11. 11 from Page.PageObject.LoginPage import LoginPage

  12. 12

  13. 13

  14. 14 # 从配置文件中获取正确的用户名和密码

  15. 15 userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')

  16. 16 passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')

  17. 17 @pytest.fixture(scope='function')

  18. 18 def login(driver):

  19. 19 '''除登录用例,每一个用例的前置条件'''

  20. 20 print('------------staring login------------')

  21. 21 loginFunc = LoginPage(driver, 30)

  22. 22 loginFunc.login(userName, passWord)

  23. 23 yield

  24. 24 print('------------end login------------')

  25. 25 driver.delete_all_cookies()

ok,开始编写测试用例啦

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 14:10

  4. 4 @Auth : linux超

  5. 5 @File : test_loginCase.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import pytest

  11. 11 from Page.PageObject.LoginPage import LoginPage

  12. 12

  13. 13

  14. 14 @pytest.mark.loginTest

  15. 15 class TestLogin(object):

  16. 16

  17. 17 # 测试数据

  18. 18 loginSheet = LoginPage.getSheet('login')

  19. 19 data = LoginPage.excel.getAllValuesOfSheet(loginSheet)

  20. 20

  21. 21 # 正确的帐号和密码

  22. 22 userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')

  23. 23 passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')

  24. 24

  25. 25 @pytest.fixture()

  26. 26 def teardown_func(self, driver):

  27. 27 """

  28. 28 执行每个用例之后要清除一下cookie,

  29. 29 否则你第一个账号登录之后,重新加载网址还是登录状态,无法测试后面的账号

  30. 30 """

  31. 31 yield

  32. 32 driver.delete_all_cookies()

  33. 33

  34. 34 @pytest.mark.parametrize('username, password, expect', data)

  35. 35 def test_login(self, teardown_func, driver, username, password, expect):

  36. 36 """测试登录"""

  37. 37 login = LoginPage(driver, 30)

  38. 38 login.login(username, password)

  39. 39 login.sleep(5)

  40. 40 # 增加登录失败时, 对提示信息的验证

  41. 41 if username == TestLogin.userName and password == TestLogin.passWord:

  42. 42 login.assertValueInSource(expect)

  43. 43 elif username == '':

  44. 44 login.assertTextEqString(expect)

  45. 45 elif username != '' and password == '':

  46. 46 login.assertTextEqString(expect)

  47. 47 elif username == '' and password == '':

  48. 48 login.assertTextEqString(expect)

  49. 49 else:

  50. 50 login.assertTextEqString(expect)

  51. 51

  52. 52

  53. 53 if __name__ == "__main__":

  54. 54 pytest.main(['-v', 'test_loginCase.py'])

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 16:15

  4. 4 @Auth : linux超

  5. 5 @File : test_contactCase.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import re

  11. 11 import pytest

  12. 12 from Page.PageObject.HomePage import HomePage

  13. 13 from Page.PageObject.ContactPage import ContactPage

  14. 14

  15. 15

  16. 16 @pytest.mark.conatctTest

  17. 17 class TestAddContact(object):

  18. 18

  19. 19 # 测试数据

  20. 20 contactSheet = ContactPage.getSheet('contact')

  21. 21 data = ContactPage.excel.getAllValuesOfSheet(contactSheet)

  22. 22

  23. 23 @pytest.mark.newcontact

  24. 24 @pytest.mark.parametrize('Name, Mail, Star, Phone, Comment, expect', data)

  25. 25 def test_NewContact(self, driver, login, Name, Mail, Star, Phone, Comment, expect):

  26. 26 """测试添加联系人"""

  27. 27 home_page = HomePage(driver)

  28. 28 contact_page = ContactPage(driver)

  29. 29 home_page.selectMenu()

  30. 30 contact_page.newContact(Name, Mail, Star, Phone, Comment)

  31. 31 home_page.sleep(5)

  32. 32 # 校验错误的邮箱是否提示信息正确

  33. 33 if re.match(r'^.{1,}@[0-9a-zA-Z]{1,13}\..*$', Mail):

  34. 34 contact_page.assertValueInSource(expect)

  35. 35 else:

  36. 36 contact_page.assertErrorTip(expect)

  37. 37

  38. 38 if __name__ == '__main__':

  39. 39 pytest.main(['-v', 'test_contactCase.py'])

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 10:04

  4. 4 @Auth : linux超

  5. 5 @File : test_sendMailCase.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import pytest

  11. 11 from Page.PageObject.SendMailPage import SendMailPage

  12. 12

  13. 13 @pytest.mark.sendMailTest

  14. 14 class TestSendMail(object):

  15. 15

  16. 16 sendMailSheet = SendMailPage.getSheet('mail')

  17. 17 data = SendMailPage.excel.getAllValuesOfSheet(sendMailSheet)

  18. 18

  19. 19 @pytest.mark.sendmail

  20. 20 @pytest.mark.parametrize('Address, Subject, Text, PFA', data)

  21. 21 def test_sendMail(self, driver, login, Address, Subject, Text,PFA):

  22. 22 """测试发送邮件,包括带附件的邮件"""

  23. 23 send_mail = SendMailPage(driver)

  24. 24 send_mail.sendMail(Address, Subject, Text, PFA)

  25. 25 send_mail.sleep(5)

  26. 26 assert send_mail.isElementExsit(*SendMailPage.expect)

  27. 27

  28. 28 if __name__=='__main__':

  29. 29 pytest.main(['-v', 'test_sendMailCase.py'])

问题

用例已经写完了,有两个问题

1.有没有发现我们的报告怎么生成的?也没有失败用例截图?

2.我们貌似并没有编写驱动浏览器的代码?

现在我们来解决这个两个问题根据pytest的conftest.py文件的原理,我们可以把驱动浏览器的代码写在一个全局的conftest.py文件里面。报告生成其实是通过命令 pytest --html=‘report.html’ --self-contained-html生成的,但是这样的报告对用例的描述不是很清晰,且没有对失败用例截图,也不方便我们分析项目的缺陷,我们也可以填写代码放到这个文件里面

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/12 14:10

  4. 4 @Auth : linux超

  5. 5 @File : conftest.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import pytest

  11. 11 from selenium import webdriver

  12. 12 from py._xmlgen import html

  13. 13

  14. 14 _driver = None

  15. 15 # 测试失败时添加截图和测试用例描述(用例的注释信息)

  16. 16

  17. 17 @pytest.mark.hookwrapper

  18. 18 def pytest_runtest_makereport(item):

  19. 19 """

  20. 20 当测试失败的时候,自动截图,展示到html报告中

  21. 21 :param item:

  22. 22 """

  23. 23 pytest_html = item.config.pluginmanager.getplugin('html')

  24. 24 outcome = yield

  25. 25 report = outcome.get_result()

  26. 26 extra = getattr(report, 'extra', [])

  27. 27

  28. 28 if report.when == 'call' or report.when == "setup":

  29. 29 xfail = hasattr(report, 'wasxfail')

  30. 30 if (report.skipped and xfail) or (report.failed and not xfail):

  31. 31 file_name = report.nodeid.replace("::", "_")+".png"

  32. 32 screen_img = _capture_screenshot()

  33. 33 if file_name:

  34. 34 html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \

  35. 35 'onclick="window.open(this.src)" align="right"/></div>' % screen_img

  36. 36 extra.append(pytest_html.extras.html(html))

  37. 37 report.extra = extra

  38. 38 report.description = str(item.function.__doc__)

  39. 39 report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")

  40. 40

  41. 41

  42. 42 @pytest.mark.optionalhook

  43. 43 def pytest_html_results_table_header(cells):

  44. 44 cells.insert(1, html.th('Description'))

  45. 45 cells.insert(2, html.th('Test_nodeid'))

  46. 46 cells.pop(2)

  47. 47

  48. 48

  49. 49 @pytest.mark.optionalhook

  50. 50 def pytest_html_results_table_row(report, cells):

  51. 51 cells.insert(1, html.td(report.description))

  52. 52 cells.insert(2, html.td(report.nodeid))

  53. 53 cells.pop(2)

  54. 54

  55. 55

  56. 56 def _capture_screenshot():

  57. 57 """

  58. 58 截图保存为base64

  59. 59 :return:

  60. 60 """

  61. 61 return _driver.get_screenshot_as_base64()

  62. 62 # 这里我设置的级别是模块级别,也就是每个测试文件运行一次

  63. 63 # 可以设置为session,全部用例执行一次,但是针对126邮箱的话

  64. 64 # 登录次数太多会叫你验证,如果验证就没法执行用例了,我没有对验证处理(处理比较复杂)

  65. 65

  66. 66

  67. 67 @pytest.fixture(scope='module')

  68. 68 def driver():

  69. 69 global _driver

  70. 70 print('------------open browser------------')

  71. 71 _driver = webdriver.Firefox()

  72. 72

  73. 73 yield _driver

  74. 74 print('------------close browser------------')

  75. 75 _driver.quit()

最后呢,为了减小项目维护成本,我们把一些全局的配置项,放到我们的功能配置文件中共全局使用,包括运行用例的一些命令字符串,可以自行修改

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/20 16:50

  4. 4 @Auth : linux超

  5. 5 @File : conf.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 from datetime import datetime

  11. 11 import os

  12. 12 # 项目根目录

  13. 13 projectDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

  14. 14 # 报告目录

  15. 15 reportDir = os.path.join(projectDir, 'report')

  16. 16 # ui对象库config.ini文件所在目录

  17. 17 configDir = os.path.join(projectDir, 'config', 'config.ini')

  18. 18 # 测试数据所在目录

  19. 19 excelPath = os.path.join(projectDir, 'data', 'tcData.xlsx')

  20. 20 # 当前时间

  21. 21 currentTime = datetime.now().strftime('%H_%M_%S')

  22. 22

  23. 23 # 邮件配置信息

  24. 24 # 邮件服务器

  25. 25 smtpServer = 'smtp.qq.com'

  26. 26 # 发送者

  27. 27 fromUser = '账号@qq.com'

  28. 28 # 发送者密码

  29. 29 fromPassWord = 'mhxvqpewblldbjhf'

  30. 30 # 接收者

  31. 31 toUser = ['账号@qq.com']# 可以同时发送给多人,追加到列表中

  32. 32 # 邮件标题

  33. 33 subject = 'xx项目自动化测试报告'

  34. 34 # 邮件正文

  35. 35 contents = '测试报告正文'

  36. 36 # 报告名称

  37. 37 htmlName = r'{}\testReport{}.html'.format(reportDir, currentTime)

  38. 38

  39. 39 # 脚本执行命令

  40. 40 args = r'pytest --html=' + htmlName+ ' ' + '--self-contained-html'

  41. 41 # modify by linuxchao at 2019/4/25

  42. 42 args_login = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'loginTest'+ ' --self-contained-html'

  43. 43 args_contact = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'contactTest'+ ' --self-contained-html'

  44. 44 args_sendmail = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'sendMailTest'+ ' --self-contained-html'

运行项目

通过命令运行

1.cmd切换到项目的根目录,执行pytest --html=‘report.html’ --self-contained-html命令(此运行方式,无法发送测试报告邮件)这种方式感觉有点low,我们换另外一种方式,可以通过os模块自动执行相关命令,编写运行用例代码

 
  1. 1 """

  2. 2 ------------------------------------

  3. 3 @Time : 2019/4/15 16:14

  4. 4 @Auth : linux超

  5. 5 @File : RunTestCase.py

  6. 6 @IDE : PyCharm

  7. 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!

  8. 8 ------------------------------------

  9. 9 """

  10. 10 import sys

  11. 11 sys.path.append('.')

  12. 12 from config.conf import *

  13. 13 from util.sendMailForReprot import SendMailWithReport

  14. 14

  15. 15

  16. 16 def main():

  17. 17 # 判断项目的根目录是否在sys.path中,没有就添加

  18. 18 if projectDir not in sys.path:

  19. 19 sys.path.append(projectDir)

  20. 20 # 执行用例

  21. 21 os.system(args)

  22. 22 # 发送邮件

  23. 23 SendMailWithReport.send_mail(

  24. 24 smtpServer, fromUser, fromPassWord,

  25. 25 toUser, subject, contents,

  26. 26 htmlName)

  27. 27

  28. 28

  29. 29 if __name__ == '__main__':

  30. 30 main()

我们可以直接执行这个文件执行所用的测试用例了!

其实我们运行用例往往不只是 使用pytest --html=‘report.html’ --self-contained-html 这样一个简单的命令运行,通常会添加很多的命令选项,比如-v,-q,-s等等,那么怎么办呢?这时候就用到了pytest.ini配置文件了

只添加了几个简单的命令选项

 
  1. 1 [pytest]

  2. 2 addopts=-vqs

  3. 3 testpaths=./TestCases

  4. 4 markers=

  5. 5 loginTest: Run login test cases

  6. 6 contactTest: Run add contact test cases

  7. 7 sendMailTest: Run send mail test cases

测试输出

1.自动生成html格式报告,其中报告里面附带用例执行日志明细,及用例失败自动截图(部分报告展示)

2.自动发送测试邮件给指定用户

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

 

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

  • 19
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值