Pytest UI自动化测试实战实例

环境准备

项目简介
测试地址
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 PytestAutoTestFrameWork
 2 |—|config
 3 |——|__init__.py
 4 |——|conf.py
 5 |——|config.ini
 6 |—|data
 7 |——|__init__.py
 8 |——|tcData.xlsx
 9 |—Page
10 |——|PageObject.py
11 |———|__init__.py
12 |———|ContactPage.py
13 |———|HomePage.py
14 |———|LoginPage.py
15 |———|SendMailPage.py
16 |——|__init__.py
17 |——|BasePage.py
18 |—|report
19 |—|TestCases
20 |——|__init__.py
21 |——|conftest.py
22 |——|test_confactCase.py
23 |——|test_loginCase.py
24 |——|test_sendMailCase.py
25 |—|util
26 |——|__init__.py
27 |——|clipboard.py
28 |——|keyboard.py
29 |——|parseConFile.py
30 |——|parseExcelFile.py
31 |——|sendMailForReport.py
32 |—|conftest.py
33 |—|pytest.ini
34 |—|RunTestCase.py
代码实现

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

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/15 12:04
 4 @Auth : linux超
 5 @File : clipboard.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import win32con
11 import win32clipboard as WC
12 
13 
14 class ClipBoard(object):
15     '''设置剪切板内容和获取剪切板内容'''
16 
17     @staticmethod
18     def getText():
19         '''获取剪切板的内容'''
20         WC.OpenClipboard()
21         value = WC.GetClipboardData(win32con.CF_TEXT)
22         WC.CloseClipboard()
23         return value
24 
25     @staticmethod
26     def setText(value):
27         '''设置剪切板的内容'''
28         WC.OpenClipboard()
29         WC.EmptyClipboard()
30         WC.SetClipboardData(win32con.CF_UNICODETEXT, value)
31         WC.CloseClipboard()
32 
33 
34 if __name__ == '__main__':
35     from selenium import webdriver
36 
37     value = 'python'
38     driver = webdriver.Firefox()
39     driver.get('http://www.baidu.com')
40     query = driver.find_element_by_id('kw')
41     ClipBoard.setText(value)
42     clValue = ClipBoard.getText()
43     query.send_keys(clValue.decode('utf-8'))
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/15 12:05
 4 @Auth : linux超
 5 @File : keyboard.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 
11 # 模拟按键
12 import win32api
13 import win32con
14 import time
15 
16 
17 class KeyBoard(object):
18     """模拟按键"""
19     # 键盘码
20     vk_code = {
21         'enter' : 0x0D,
22         'tab' : 0x09,
23         'ctrl' : 0x11,
24         'v' : 0x56,
25         'a' : 0x41,
26         'x' : 0x58
27     }
28 
29     @staticmethod
30     def keyDown(key_name):
31         """按下键"""
32         key_name = key_name.lower()
33         try:
34             win32api.keybd_event(KeyBoard.vk_code[key_name], 0, 0, 0)
35         except Exception as e:
36             print('未按下enter键')
37             print(e)
38 
39     @staticmethod
40     def keyUp(key_name):
41         """抬起键"""
42         key_name = key_name.lower()
43         win32api.keybd_event(KeyBoard.vk_code[key_name], 0, win32con.KEYEVENTF_KEYUP, 0)
44 
45     @staticmethod
46     def oneKey(key):
47         """模拟单个按键"""
48         key = key.lower()
49         KeyBoard.keyDown(key)
50         time.sleep(2)
51         KeyBoard.keyUp(key)
52 
53     @staticmethod
54     def twoKeys(key1, key2):
55         """模拟组合按键"""
56         key1 = key1.lower()
57         key2 = key2.lower()
58         KeyBoard.keyDown(key1)
59         KeyBoard.keyDown(key2)
60         KeyBoard.keyUp(key1)
61         KeyBoard.keyUp(key2)
62 
63 
64 if __name__ == '__main__':
65     from selenium import webdriver
66     driver = webdriver.Firefox()
67     driver.get('http://www.baidu.com')
68     driver.find_element_by_id('kw').send_keys('python')
69     KeyBoard.twoKeys('ctrl', 'a')
70     KeyBoard.twoKeys('ctrl', 'x')

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

 
  1 """
  2 ------------------------------------
  3 @Time : 2019/4/22 16:12
  4 @Auth : linux超
  5 @File : parseExcelFile.py
  6 @IDE  : PyCharm
  7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
  8 ------------------------------------
  9 """
 10 from openpyxl import load_workbook
 11 from config.conf import excelPath
 12 
 13 
 14 class ParseExcel(object):
 15 
 16     def __init__(self):
 17         self.wk = load_workbook(excelPath)
 18         self.excelFile = excelPath
 19 
 20     def getSheetByName(self, sheetName):
 21         """获取sheet对象"""
 22         sheet = self.wk[sheetName]
 23         return sheet
 24 
 25     def getRowNum(self, sheet):
 26         """获取有效数据的最大行号"""
 27         return sheet.max_row
 28 
 29     def getColsNum(self, sheet):
 30         """获取有效数据的最大列号"""
 31         return sheet.max_column
 32 
 33     def getRowValues(self, sheet, rowNum):
 34         """获取某一行的数据"""
 35         maxColsNum = self.getColsNum(sheet)
 36         rowValues = []
 37         for colsNum in range(1, maxColsNum + 1):
 38             value = sheet.cell(rowNum, colsNum).value
 39             if value is None:
 40                 value = ''
 41             rowValues.append(value)
 42         return tuple(rowValues)
 43 
 44     def getColumnValues(self, sheet, columnNum):
 45         """获取某一列的数据"""
 46         maxRowNum = self.getRowNum(sheet)
 47         columnValues = []
 48         for rowNum in range(2, maxRowNum + 1):
 49             value = sheet.cell(rowNum, columnNum).value
 50             if value is None:
 51                 value = ''
 52             columnValues.append(value)
 53         return tuple(columnValues)
 54 
 55     def getValueOfCell(self, sheet, rowNum, columnNum):
 56         """获取某一个单元格的数据"""
 57         value = sheet.cell(rowNum, columnNum).value
 58         if value is None:
 59             value = ''
 60         return value
 61 
 62     def getAllValuesOfSheet(self, sheet):
 63         """获取某一个sheet页的所有测试数据,返回一个元祖组成的列表"""
 64         maxRowNum = self.getRowNum(sheet)
 65         columnNum = self.getColsNum(sheet)
 66         allValues = []
 67         for row in range(2, maxRowNum + 1):
 68             rowValues = []
 69             for column in range(1, columnNum + 1):
 70                 value = sheet.cell(row, column).value
 71                 if value is None:
 72                     value = ''
 73                 rowValues.append(value)
 74             allValues.append(tuple(rowValues))
 75         return allValues
 76 
 77 
 78 if __name__ == '__main__':
 79     # excel = ParseExcel()
 80     # sheet = excel.getSheetByName('login')
 81     # print('行号:', excel.getRowNum(sheet))
 82     # print('列号:', excel.getColsNum(sheet))
 83     #
 84     # rowvalues = excel.getRowValues(sheet, 1)
 85     # columnvalues = excel.getColumnValues(sheet, 2)
 86     # valueofcell = excel.getValueOfCell(sheet, 1, 2)
 87     # allvalues = excel.getAllValuesOfSheet(sheet)
 88     #
 89     # print('第{}行数据{}'.format(1, rowvalues))
 90     # print('第{}列数据{}'.format(2, columnvalues))
 91     # print('{}{}单元格的内容{}'.format(1, 2, valueofcell))
 92     # print('login{}'.format(allvalues))
 93 
 94     excel = ParseExcel()
 95     sheet = excel.getSheetByName('mail')
 96     print('行号:', excel.getRowNum(sheet))
 97     print('列号:', excel.getColsNum(sheet))
 98 
 99     allvalues = excel.getAllValuesOfSheet(sheet)
100 
101     print('sendmail{}'.format(allvalues))
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/18 10:54
 4 @Auth : linux超
 5 @File : parseConFile.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import configparser
11 from config.conf import configDir
12 
13 
14 class ParseConFile(object):
15 
16     def __init__(self):
17         self.file = configDir
18         self.conf = configparser.ConfigParser()
19         self.conf.read(self.file, encoding='utf-8')
20 
21     def getAllSections(self):
22         """获取所有的section,返回一个列表"""
23         return self.conf.sections()
24 
25     def getAllOptions(self, section):
26         """获取指定section下所有的option, 返回列表"""
27         return self.conf.options(section)
28 
29     def getLocatorsOrAccount(self, section, option):
30         """获取指定section, 指定option对应的数据, 返回元祖和字符串"""
31         try:
32             locator = self.conf.get(section, option)
33             if ('->' in locator):
34                 locator = tuple(locator.split('->'))
35             return locator
36         except configparser.NoOptionError as e:
37             print('error:', e)
38         return 'error: No option "{}" in section: "{}"'.format(option, section)
39 
40     def getOptionValue(self, section):
41         """获取指定section下所有的option和对应的数据,返回字典"""
42         value = dict(self.conf.items(section))
43         return value
44 
45 
46 if __name__ == '__main__':
47     cf = ParseConFile()
48     print(cf.getAllSections())
49     print(cf.getAllOptions('126LoginAccount'))
50     print(cf.getLocatorsOrAccount('126LoginAccount', 'username'))
51     print(cf.getOptionValue('126LoginAccount'))
1 [126LoginAccount];126邮箱正确的登录账号和密码;运行用例时请更换正确的用户名和密码
 2 username=linuxxiaochao
 3 password=xiaochao11520
 4 [HomePageElements];126邮箱首页菜单栏元素
 5 homePage=id->_mail_tabitem_0_3text
 6 mailList=id->_mail_tabitem_1_4text
 7 applicationCenter=id->_mail_tabitem_2_5text
 8 inBox=id->_mail_tabitem_3_6text
 9 [LoginPageElements];126邮箱登录页面的元素
10 frame=xpath->//div[@id="loginDiv"]/iframe
11 username=xpath->//input[@name="email"]
12 password=xpath->//input[@name="password"]
13 loginBtn=xpath->//a[@id="dologin"]
14 ferrorHead=xpath->//div[@class="ferrorhead"]
15 [ContactPageElements];126邮箱添加联系人页面元素
16 new_contact=xpath->//span[text()="新建联系人"]
17 name=id->input_N
18 mail=xpath->//div[@id="iaddress_MAIL_wrap"]//input[@class="nui-ipt-input"]
19 star=xpath->//span[@class="nui-chk-text"]/preceding-sibling::span/b
20 phone=xpath->//div[@id='iaddress_TEL_wrap']//input[@class='nui-ipt-input']
21 comment=id->input_DETAIL
22 commit=xpath->//span[text()='确 定']
23 tooltip=xpath->//span[text()='请正确填写邮件地址。']
24 [SendMailPageElements];126邮箱发送邮件页面元素
25 writeMail=xpath->//div[@id='dvNavContainer']//span[text()='写 信']
26 addressee=xpath->//input[@aria-label='收件人地址输入框,请输入邮件地址,多人时地址请以分号隔开']
27 subject=xpath->//input[contains(@id, '_subjectInput')]
28 iframe=xpath->//iframe[@class="APP-editor-iframe"]
29 text=xpath->/html/body
30 sendBtn=xpath->//header//span[text()='发送']
31 expect=xpath->//h1[contains(@id,'_succInfo')]
32 uploadAttachment=xpath->//div[@title="点击添加附件"]
33 delete=xpath->//a[text()='删除']
新建excel文件,分3个sheet,分别为:login,contact,mail #每个sheet中数据可自行填写,驱动测试用例执行不同的数据进行测试

login

contact

 mail

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

 
  1 """
  2 ------------------------------------
  3 @Time : 2019/4/20 8:45
  4 @Auth : linux超
  5 @File : BasePage.py
  6 @IDE  : PyCharm
  7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
  8 ------------------------------------
  9 """
 10 import time
 11 from selenium.webdriver.support import expected_conditions as EC
 12 from selenium.webdriver.support.wait import WebDriverWait as wd
 13 from selenium.webdriver.common.by import By
 14 from selenium.common.exceptions import NoSuchWindowException, TimeoutException, \
 15     NoAlertPresentException, NoSuchFrameException
 16 from selenium import webdriver
 17 
 18 from util.clipboard import ClipBoard
 19 from util.keyboard import KeyBoard
 20 from util.parseConFile import ParseConFile
 21 from util.parseExcelFile import ParseExcel
 22 
 23 
 24 class BasePage(object):
 25     """
 26     结合显示等待封装一些selenium 内置方法
 27     """
 28     cf = ParseConFile()
 29     excel = ParseExcel()
 30 
 31     def __init__(self, driver, outTime=30):
 32         self.byDic = {
 33         'id': By.ID,
 34         'name': By.NAME,
 35         'class_name': By.CLASS_NAME,
 36         'xpath': By.XPATH,
 37         'link_text': By.LINK_TEXT
 38         }
 39         self.driver = driver
 40         self.outTime = outTime
 41 
 42     def findElement(self, by, locator):
 43         """
 44         find alone element
 45         :param by: eg: id, name, xpath, css.....
 46         :param locator: id, name, xpath for str
 47         :return: element object
 48         """
 49         try:
 50             print('[Info:Starting find the element "{}" by "{}"!]'.format(locator, by))
 51             element = wd(self.driver, self.outTime).until(lambda x : x.find_element(by, locator))
 52         except TimeoutException as t:
 53             print('error: found "{}" timeout!'.format(locator), t)
 54         except NoSuchWindowException as e:
 55             print('error: no such "{}"'.format(locator), e)
 56         except Exception as e:
 57             raise e
 58         else:
 59             # print('[Info:Had found the element "{}" by "{}"!]'.format(locator, by))
 60             return element
 61 
 62     def findElements(self, by, locator):
 63         """
 64         find group elements
 65         :param by: eg: id, name, xpath, css.....
 66         :param locator: eg: id, name, xpath for str
 67         :return: elements object
 68         """
 69         try:
 70             print('[Info:start find the elements "{}" by "{}"!]'.format(locator, by))
 71             elements = wd(self.driver, self.outTime).until(lambda x : x.find_element(by, locator))
 72         except TimeoutException as t:
 73             print(t)
 74         except NoSuchWindowException as e:
 75             print(e)
 76         except Exception as e:
 77             raise e
 78         else:
 79             # print('[Info:Had found the elements "{}" by "{}"!]'.format(locator, by))
 80             return elements
 81 
 82     def isElementExsit(self, by, locator):
 83         """
 84         assert element if exist
 85         :param by: eg: id, name, xpath, css.....
 86         :param locator: eg: id, name, xpath for str
 87         :return: if element return True else return false
 88         """
 89         if by.lower() in self.byDic:
 90             try:
 91                 wd(self.driver, self.outTime).\
 92                     until(EC.visibility_of_element_located((self.byDic[by], locator)))
 93             except TimeoutException:
 94                 print('Error: element "{}" time out!'.format(locator))
 95                 return False
 96             except NoSuchWindowException:
 97                 print('Error: element "{}" not exsit!'.format(locator))
 98                 return False
 99             return True
100         else:
101             print('the "{}" error!'.format(by))
102 
103     def isClick(self, by, locator):
104         """判断是否可点击,返回元素对象"""
105         if by.lower() in self.byDic:
106             try:
107                 element = wd(self.driver, self.outTime).\
108                     until(EC.element_to_be_clickable((self.byDic[by], locator)))
109             except Exception:
110                 return False
111             return element
112         else:
113             print('the "{}" error!'.format(by))
114 
115     def isAlertAndSwitchToIt(self):
116         """
117         assert alert if exsit
118         :return: alert obj
119         """
120         try:
121             re = wd(self.driver, self.outTime).until(EC.alert_is_present())
122         except NoAlertPresentException:
123             return False
124         except Exception:
125             return False
126         return re
127 
128     def switchToFrame(self, by, locator):
129         """判断frame是否存在,存在就跳到frame"""
130         print('info:switching to iframe "{}"'.format(locator))
131         if by.lower() in self.byDic:
132             try:
133                 wd(self.driver, self.outTime).\
134                     until(EC.frame_to_be_available_and_switch_to_it((self.byDic[by], locator)))
135             except TimeoutException as t:
136                 print('error: found "{}" timeout!'.format(locator), t)
137             except NoSuchFrameException as e:
138                 print('error: no such "{}"'.format(locator), e)
139             except Exception as e:
140                 raise e
141         else:
142             print('the "{}" error!'.format(by))
143 
144     def switchToDefaultFrame(self):
145         """返回默认的frame"""
146         print('info:switch back to default iframe')
147         try:
148             self.driver.switch_to.default_content()
149         except Exception as e:
150             print(e)
151 
152     def getAlertText(self):
153         """获取alert的提示信息"""
154         if self.isAlertAndSwitchToIt():
155             alert = self.isAlertAndSwitchToIt()
156             return alert.text
157         else:
158             return None
159 
160     def getElementText(self, by, locator, name=None):
161         """获取某一个元素的text信息"""
162         try:
163             element = self.findElement(by, locator)
164             if name:
165                 return element.get_attribute(name)
166             else:
167                 return element.text
168         except:
169             print('get "{}" text failed return None'.format(locator))
170             return None
171 
172     def loadUrl(self, url):
173         """加载url"""
174         print('info: string upload url "{}"'.format(url))
175         self.driver.get(url)
176 
177     def getSource(self):
178         """获取页面源码"""
179         return self.driver.page_source
180 
181     def sendKeys(self, by, locator, value=''):
182         """写数据"""
183         print('info:input "{}"'.format(value))
184         try:
185             element = self.findElement(by, locator)
186             element.send_keys(value)
187         except AttributeError as e:
188             print(e)
189 
190     def clear(self, by, locator):
191         """清理数据"""
192         print('info:clearing value')
193         try:
194             element = self.findElement(by, locator)
195             element.clear()
196         except AttributeError as e:
197             print(e)
198 
199     def click(self, by, locator):
200         """点击某个元素"""
201         print('info:click "{}"'.format(locator))
202         element = self.isClick(by, locator)
203         if element:
204             element.click()
205         else:
206             print('the "{}" unclickable!')
207 
208     def sleep(self, num=0):
209         """强制等待"""
210         print('info:sleep "{}" minutes'.format(num))
211         time.sleep(num)
212 
213     def ctrlV(self, value):
214         """ctrl + V 粘贴"""
215         print('info:pasting "{}"'.format(value))
216         ClipBoard.setText(value)
217         self.sleep(3)
218         KeyBoard.twoKeys('ctrl', 'v')
219 
220     def enterKey(self):
221         """enter 回车键"""
222         print('info:keydown enter')
223         KeyBoard.oneKey('enter')
224 
225     def waitElementtobelocated(self, by, locator):
226         """显示等待某个元素出现,且可见"""
227         print('info:waiting "{}" to be located'.format(locator))
228         try:
229             wd(self.driver, self.outTime).until(EC.visibility_of_element_located((self.byDic[by], locator)))
230         except TimeoutException as t:
231             print('error: found "{}" timeout!'.format(locator), t)
232         except NoSuchWindowException as e:
233             print('error: no such "{}"'.format(locator), e)
234         except Exception as e:
235             raise e
236 
237     def assertValueInSource(self, value):
238         """断言某个关键字是否存在页面源码中"""
239         print('info:assert "{}" in page source'.format(value))
240         source = self.getSource()
241         assert value in source, '关键字"{}"不存在源码中!'.format(value)
242 
243     def assertStringContainsValue(self, String, value):
244         """断言某段字符串包含另一个字符串"""
245         print('info:assert "{}" contains "{}"'.format(String, value))
246         assert value in String, '"{}"不包含"{}"!'.format(String, value)
247 
248 
249     @staticmethod
250     def getSheet(sheetName):
251         """获取某个sheet页的对象"""
252         sheet = BasePage.excel.getSheetByName(sheetName)
253         return sheet
254 
255 
256 if __name__ == "__main__":
257     driver = webdriver.Firefox()
258     frame = ('xpath', '//div[@id="loginDiv"]/ifram')
259     wait = BasePage(driver)
260     driver.get('https://mail.126.com/')
261     wait.switchToFrame(*frame)
262     username = wait.findElement('xpath', '//input[@name="email"]')
263     username.send_keys('账号')
264     if wait.isElementExsit('xpath', '//input[@name="password"]'):
265         wait.findElement('xpath', '//input[@name="password"]').send_keys('xiaochao11520')
266     wait.click('xpath', '//a[@id="dologin"]')

 

 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 12:28
 4 @Auth : linux超
 5 @File : HomePage.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 from Page.BasePage import BasePage
11 
12 
13 class HomePage(BasePage):
14     # 配置文件读取元素
15     homePage = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'homePage')
16     mailList = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'mailList')
17     applicationCenter = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'applicationCenter')
18     inBox = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'inBox')
19     '''首页菜单选项'''
20     def selectMenu(self, Menu='mailList'):
21         """邮箱首页选择菜单"""
22         if Menu == 'mailList':
23             self.click(*HomePage.mailList)
24         elif Menu == 'homePage':
25             self.click(*HomePage.homePage)
26         elif Menu == 'applicationCenter':
27             self.click(*HomePage.applicationCenter)
28         elif Menu == 'inBox':
29             self.click(*HomePage.inBox)
30         else:
31             raise ValueError('''
32             菜单选择错误!
33             homePage->首页
34             mailList->通讯录
35             applicationCenter->应用中心
36             inBox->收件箱''')
37 
38 if __name__=='__main__':
39     from selenium import webdriver
40     from Page.PageObject.LoginPage import LoginPage
41     driver = webdriver.Firefox()
42     login = LoginPage(driver)
43     login.login('账号', 'xiaochao11520')
44 
45     home = HomePage(driver)
46     home.selectMenu()
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 12:28
 4 @Auth : linux超
 5 @File : LoginPage.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 from Page.BasePage import BasePage
11 
12 
13 class LoginPage(BasePage):
14 
15     # 配置文件读取元素
16     frame = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'frame')
17     username = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'username')
18     password = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'password')
19     loginBtn = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'loginBtn')
20     ferrorHead = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'ferrorHead')  # 登录失败提示
21 
22     def login(self, userName, passWord):
23         '''登录'''
24         print('-------staring login-------')
25         self.loadUrl('https://mail.126.com')
26         self.switchToFrame(*LoginPage.frame)
27         self.clear(*LoginPage.username)
28         self.sendKeys(*LoginPage.username, userName)
29         self.clear(*LoginPage.password)
30         self.sendKeys(*LoginPage.password, passWord)
31         self.click(*LoginPage.loginBtn)
32         self.switchToDefaultFrame()
33         print('---------end login---------')
34 
35     # add at 2019/04/19
36     def assertTextEqString(self, expected, name = None):
37         '''断言提示信息是否与期望的值相等'''
38         self.switchToFrame(*LoginPage.frame)
39         text = self.getElementText(*LoginPage.ferrorHead, name)
40         self.switchToDefaultFrame()
41         print('info: assert "{}" == "{}"'.format(text, expected))
42         assert text == expected, '{} != {}'.format(text, expected)
43 
44 if __name__=="__main__":
45     from selenium import webdriver
46     driver = webdriver.Firefox()
47     login = LoginPage(driver, 30)
48     login.login('lin', '')
49     login.assertTextEqString('请输入密码')
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 12:29
 4 @Auth : linux超
 5 @File : ContactPage.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 from Page.BasePage import BasePage
11 
12 
13 class ContactPage(BasePage):
14     # 配置文件读取元素
15     new_contact = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'new_contact')
16     name = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'name')
17     mail = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'mail')
18     star = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'star')
19     phone = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'phone')
20     comment = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'comment')
21     commit = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'commit')
22     errortip = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'tooltip')  # 错误提示
23 
24     def newContact(self, Name, Mail, Star, Phone, Comment):
25         """添加联系人"""
26         print('--------string add contact--------')
27         self.click(*ContactPage.new_contact)
28         self.sendKeys(*ContactPage.name, Name)
29         self.sendKeys(*ContactPage.mail, Mail)
30         if Star == '1':
31             self.click(*ContactPage.star)
32         self.sendKeys(*ContactPage.phone, Phone)
33         self.sendKeys(*ContactPage.comment, Comment)
34         self.click(*ContactPage.commit)
35         print('--------end add contact--------')
36 
37     def assertErrorTip(self, excepted):
38         """断言联系人添加失败时是否有提示信息"""
39         text = self.getElementText(*ContactPage.errortip)
40         print('info: assert "{}"=="{}"'.format(text, excepted))
41         assert text == excepted
42 
43 if __name__ == '__main__':
44     from selenium import webdriver
45     from Page.PageObject.LoginPage import LoginPage
46     from Page.PageObject.HomePage import HomePage
47     driver = webdriver.Firefox()
48     home = HomePage(driver)
49     login = LoginPage(driver)
50     contact = ContactPage(driver)
51 
52     login.login('账号', 'xiaochao11520')
53     home.selectMenu()
54     contact.newContact('281754041@qq.com')
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 9:16
 4 @Auth : linux超
 5 @File : SendMailPage.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 from Page.BasePage import BasePage
11 
12 
13 class SendMailPage(BasePage):
14     # 配置文件读取元素
15     writeMail = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'writeMail')
16     addressee = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'addressee')
17     subject = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'subject')
18     iframe = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'iframe')
19     text = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'text')
20     sendBtn = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'sendBtn')
21     expect = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'expect')
22     uploadAttachment = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'uploadAttachment')
23     delete = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'delete')
24 
25     def sendMail(self, Address, Subject, Text, PFA=''):
26         """发送邮件功能"""
27         print('------------string send mail---------------------')
28         self.click(*SendMailPage.writeMail)
29         self.sendKeys(*SendMailPage.addressee, Address)
30         self.sendKeys(*SendMailPage.subject, Subject)
31         self.switchToFrame(*SendMailPage.iframe)
32         self.sendKeys(*SendMailPage.text, Text)
33         self.switchToDefaultFrame()
34         if PFA:
35             self.click(*SendMailPage.uploadAttachment)
36             self.ctrlV(PFA)
37             self.enterKey()
38             self.waitElementtobelocated(*SendMailPage.delete)
39         self.click(*SendMailPage.sendBtn)
40         print('------------end send mail---------------------')
41 
42 if __name__=='__main__':
43     from Page.PageObject.LoginPage import LoginPage
44     from selenium import webdriver
45     driver = webdriver.Firefox()
46 
47     login = LoginPage(driver)
48     login.login('账号', 'xiaochao11520')
49     sendMail = SendMailPage(driver)
50     sendMail.sendMail('281754043@qq.com', 'pytest', 'pytest实战实例', 1, 'D:\KeyWordDriverTestFrameWork\geckodriver.log')

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

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 15:10
 4 @Auth : linux超
 5 @File : conftest.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import pytest
11 from Page.PageObject.LoginPage import LoginPage
12 
13 
14 # 从配置文件中获取正确的用户名和密码
15 userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')
16 passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')
17 @pytest.fixture(scope='function')
18 def login(driver):
19     '''除登录用例,每一个用例的前置条件'''
20     print('------------staring login------------')
21     loginFunc = LoginPage(driver, 30)
22     loginFunc.login(userName, passWord)
23     yield
24     print('------------end login------------')
25     driver.delete_all_cookies()

ok,开始编写测试用例啦

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 14:10
 4 @Auth : linux超
 5 @File : test_loginCase.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import pytest
11 from Page.PageObject.LoginPage import LoginPage
12 
13 
14 @pytest.mark.loginTest
15 class TestLogin(object):
16 
17     # 测试数据
18     loginSheet = LoginPage.getSheet('login')
19     data = LoginPage.excel.getAllValuesOfSheet(loginSheet)
20 
21     # 正确的帐号和密码
22     userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')
23     passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')
24 
25     @pytest.fixture()
26     def teardown_func(self, driver):
27         """
28         执行每个用例之后要清除一下cookie,
29         否则你第一个账号登录之后,重新加载网址还是登录状态,无法测试后面的账号
30         """
31         yield
32         driver.delete_all_cookies()
33 
34     @pytest.mark.parametrize('username, password, expect', data)
35     def test_login(self, teardown_func, driver, username, password, expect):
36         """测试登录"""
37         login = LoginPage(driver, 30)
38         login.login(username, password)
39         login.sleep(5)
40         # 增加登录失败时, 对提示信息的验证
41         if username == TestLogin.userName and password == TestLogin.passWord:
42             login.assertValueInSource(expect)
43         elif username == '':
44             login.assertTextEqString(expect)
45         elif username != '' and password == '':
46             login.assertTextEqString(expect)
47         elif username == '' and password == '':
48             login.assertTextEqString(expect)
49         else:
50             login.assertTextEqString(expect)
51 
52 
53 if __name__ == "__main__":
54     pytest.main(['-v', 'test_loginCase.py'])
1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 16:15
 4 @Auth : linux超
 5 @File : test_contactCase.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import re
11 import pytest
12 from Page.PageObject.HomePage import HomePage
13 from Page.PageObject.ContactPage import ContactPage
14 
15 
16 @pytest.mark.conatctTest
17 class TestAddContact(object):
18 
19     # 测试数据
20     contactSheet = ContactPage.getSheet('contact')
21     data = ContactPage.excel.getAllValuesOfSheet(contactSheet)
22 
23     @pytest.mark.newcontact
24     @pytest.mark.parametrize('Name, Mail, Star, Phone, Comment, expect', data)
25     def test_NewContact(self, driver, login, Name, Mail, Star, Phone, Comment, expect):
26         """测试添加联系人"""
27         home_page = HomePage(driver)
28         contact_page = ContactPage(driver)
29         home_page.selectMenu()
30         contact_page.newContact(Name, Mail, Star, Phone, Comment)
31         home_page.sleep(5)
32         # 校验错误的邮箱是否提示信息正确
33         if re.match(r'^.{1,}@[0-9a-zA-Z]{1,13}\..*$', Mail):
34             contact_page.assertValueInSource(expect)
35         else:
36             contact_page.assertErrorTip(expect)
37 
38 if __name__ == '__main__':
39     pytest.main(['-v', 'test_contactCase.py'])
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 10:04
 4 @Auth : linux超
 5 @File : test_sendMailCase.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import pytest
11 from Page.PageObject.SendMailPage import SendMailPage
12 
13 @pytest.mark.sendMailTest
14 class TestSendMail(object):
15 
16     sendMailSheet = SendMailPage.getSheet('mail')
17     data = SendMailPage.excel.getAllValuesOfSheet(sendMailSheet)
18 
19     @pytest.mark.sendmail
20     @pytest.mark.parametrize('Address, Subject, Text, PFA', data)
21     def test_sendMail(self, driver, login, Address, Subject, Text,PFA):
22         """测试发送邮件,包括带附件的邮件"""
23         send_mail = SendMailPage(driver)
24         send_mail.sendMail(Address, Subject, Text, PFA)
25         send_mail.sleep(5)
26         assert send_mail.isElementExsit(*SendMailPage.expect)
27 
28 if __name__=='__main__':
29     pytest.main(['-v', 'test_sendMailCase.py'])

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

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

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

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

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/12 14:10
 4 @Auth : linux超
 5 @File : conftest.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import pytest
11 from selenium import webdriver
12 from py._xmlgen import html
13 
14 _driver = None
15 # 测试失败时添加截图和测试用例描述(用例的注释信息)
16 
17 @pytest.mark.hookwrapper
18 def pytest_runtest_makereport(item):
19     """
20     当测试失败的时候,自动截图,展示到html报告中
21     :param item:
22     """
23     pytest_html = item.config.pluginmanager.getplugin('html')
24     outcome = yield
25     report = outcome.get_result()
26     extra = getattr(report, 'extra', [])
27 
28     if report.when == 'call' or report.when == "setup":
29         xfail = hasattr(report, 'wasxfail')
30         if (report.skipped and xfail) or (report.failed and not xfail):
31             file_name = report.nodeid.replace("::", "_")+".png"
32             screen_img = _capture_screenshot()
33             if file_name:
34                 html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
35                        'onclick="window.open(this.src)" align="right"/></div>' % screen_img
36                 extra.append(pytest_html.extras.html(html))
37         report.extra = extra
38         report.description = str(item.function.__doc__)
39         report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")
40 
41 
42 @pytest.mark.optionalhook
43 def pytest_html_results_table_header(cells):
44     cells.insert(1, html.th('Description'))
45     cells.insert(2, html.th('Test_nodeid'))
46     cells.pop(2)
47 
48 
49 @pytest.mark.optionalhook
50 def pytest_html_results_table_row(report, cells):
51     cells.insert(1, html.td(report.description))
52     cells.insert(2, html.td(report.nodeid))
53     cells.pop(2)
54 
55 
56 def _capture_screenshot():
57     """
58     截图保存为base64
59     :return:
60     """
61     return _driver.get_screenshot_as_base64()
62 # 这里我设置的级别是模块级别,也就是每个测试文件运行一次
63 # 可以设置为session,全部用例执行一次,但是针对126邮箱的话
64 # 登录次数太多会叫你验证,如果验证就没法执行用例了,我没有对验证处理(处理比较复杂)
65 
66 
67 @pytest.fixture(scope='module')
68 def driver():
69     global _driver
70     print('------------open browser------------')
71     _driver = webdriver.Firefox()
72 
73     yield _driver
74     print('------------close browser------------')
75     _driver.quit()

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

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/20 16:50
 4 @Auth : linux超
 5 @File : conf.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 from datetime import datetime
11 import os
12 # 项目根目录
13 projectDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
14 # 报告目录
15 reportDir = os.path.join(projectDir, 'report')
16 # ui对象库config.ini文件所在目录
17 configDir = os.path.join(projectDir, 'config', 'config.ini')
18 # 测试数据所在目录
19 excelPath = os.path.join(projectDir, 'data', 'tcData.xlsx')
20 # 当前时间
21 currentTime = datetime.now().strftime('%H_%M_%S')
22 
23 # 邮件配置信息
24 # 邮件服务器
25 smtpServer = 'smtp.qq.com'
26 # 发送者
27 fromUser = '账号@qq.com'
28 # 发送者密码
29 fromPassWord = 'mhxvqpewblldbjhf'
30 # 接收者
31 toUser = ['账号@qq.com']# 可以同时发送给多人,追加到列表中
32 # 邮件标题
33 subject = 'xx项目自动化测试报告'
34 # 邮件正文
35 contents = '测试报告正文'
36 # 报告名称
37 htmlName = r'{}\testReport{}.html'.format(reportDir, currentTime)
38 
39 # 脚本执行命令
40 args = r'pytest --html=' + htmlName+ ' ' + '--self-contained-html'
41 # modify by linuxchao at 2019/4/25
42 args_login = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'loginTest'+ ' --self-contained-html'
43 args_contact = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'contactTest'+ ' --self-contained-html'
44 args_sendmail = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'sendMailTest'+ ' --self-contained-html'

运行项目
通过命令运行

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

 
 1 """
 2 ------------------------------------
 3 @Time : 2019/4/15 16:14
 4 @Auth : linux超
 5 @File : RunTestCase.py
 6 @IDE  : PyCharm
 7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
 8 ------------------------------------
 9 """
10 import sys
11 sys.path.append('.')
12 from config.conf import *
13 from util.sendMailForReprot import SendMailWithReport
14 
15 
16 def main():
17     # 判断项目的根目录是否在sys.path中,没有就添加
18     if projectDir not in sys.path:
19         sys.path.append(projectDir)
20     # 执行用例
21     os.system(args)
22     # 发送邮件
23     SendMailWithReport.send_mail(
24         smtpServer, fromUser, fromPassWord,
25         toUser, subject, contents,
26         htmlName)
27 
28 
29 if __name__ == '__main__':
30     main()

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

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

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

1 [pytest]
2 addopts=-vqs
3 testpaths=./TestCases
4 markers=
5     loginTest: Run login test cases
6     contactTest: Run add contact test cases
7     sendMailTest: Run send mail test cases

测试输出

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

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

项目源码 

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方进群即可自行领取。    

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QQ注册页面详细的测试用例 用例编号 需求(用例标题) 模块 步骤 预期结果 测试结果 功能点优先级 V_0001 显示输入状态 注册页面 "1、进入QQ邮箱账号申请页面 2、鼠标点击邮箱账号输入框" 光标闪动,输入框亮色 1 V_0002 页面失效 注册页面 "1、进入QQ邮箱账号申请页面 2、长时间离开页面" 提示:由于您长时间离开导致页面失效,请重新注册 1 V_0003 输入不合理邮箱账号 注册页面 "1.进入QQ邮箱账号申请页面 2.输入错误的邮箱名,如111111111 3.鼠标焦点离开邮箱账号框" 提示:邮箱格式错误 1 V_0004 输入字母自动弹出合理的邮箱账号下拉框选项 注册页面 "1.进入QQ邮箱账号申请页面 2.输入字母 如 aa 自动弹出合理下拉框选项 3.选择其中一种" 提示:创建成功 1 V_0005 输入数字自动弹出合理的邮箱账号下拉框选项 注册页面 "1.进入QQ邮箱账号申请页面 2.输入数字 如 11 自动弹出合理下拉框选项 3.选择其中一种" 提示:创建成功 1 V_0006 邮箱账号分类选项 注册页面 "1.进入QQ邮箱账号申请页面 2.鼠标焦点进入邮箱账号框" "提示:1.请输入你常用的电子邮箱 2.选择""创建邮箱""或""注册普通QQ号""" 1 V_0007 查看邮箱账号旁边分类选项的“注册普通QQ号”选项是否有用 注册页面 "1.进入QQ邮箱账号申请页面 2.点击邮箱账号旁边的“注册普通QQ号” 3.是否正确转换到“QQ账号注册”页面" 成功转换页面 1 V_0008 查看邮箱账号旁边分类选项的“创建邮箱”选项是否有用 注册页面 "1.进入QQ邮箱账号申请页面 2.点击邮箱账号旁边的“创建邮箱” 3.邮箱账号是否分为2部分组成;前半部为输入项;后半部为选择项 " 成功显示区分状态 1 V_0009 查看邮箱账号右侧选择的下拉框选项是否可用 注册页面 "1.进入QQ邮箱账号申请页面 2.点击邮箱账号旁边的“创建邮箱” 3.邮箱账号是否分为2部分组成;左半部为输入项;右半部为选择项 4.点击右半部的选项下拉框,查看是否可用" 可以正确选择其中一种 1 V_0010 邮箱账号不能为空 右侧邮箱账号结尾默认值为@qq.com 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧不输入任何符号 3.右侧随意选择一种选项 如 @qq.com 4.鼠标焦点离开邮箱账号输入框" 提示:请输入邮箱 1 V_0011 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入 空格键 3.右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提示:请输入邮箱 1 V_0012 不以字母开头的邮箱账号 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入以空格、数字、点、减号、下划线组成的邮箱账号 3.右侧随意选择一种选项 如 @qq.com 4.鼠标焦点离开邮箱账号框" 提示:必须以a-z的字母(不区分大小写)为开头 V_0013 "输入小于3位的字符邮箱账号 (不区分大小写)" 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入小于3位的字符 如 aa 3.右侧随意选择一种选项 如 @qq.com 4.鼠标焦点离开账号输入框" 提示:长度该为3-18个字符 1 V_0014 "输入大于18位的字符邮箱账号 (不区分大小写)" 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入大于18位的字符 如 aaaaa11111#####a2%] 3.右侧随意选择一种选项 如 @qq.com 4.鼠标焦点离开账号输入框" 提示:长度该为3-18个字符 1 V_0015 输入3-18位字符包含不合理使用的特殊符号(点、减号、下划线) 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入包含不合理使用特殊符号的账号 如 aaa3122s2...wa 3.右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提示:点、减号、下划线不能连续出现2次或2次以上 1 V_0016 输入3-18位字符包含合理使用的特殊符号(点、减号、下划线) 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入包含合理使用特殊符号的账号 如 aaa-312_2s2.wa 3.右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提示:创建成功 1 V_0017 检测账号是否已被注册 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入3-18个合理字符 如 aaaaa222222 3.右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提示:账号以被注册 1 V_0018 注册页面 "1.进入QQ邮箱账号申请页面 2.左侧输入3-18个合理字符 如 akuhkj523312 3.右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提示:创建成功 1 V_0019 输入以点、减号、下划线为结尾 注册页面 "1、进入邮箱账号申请页面 2、左侧输入以点、减号、下划线为结尾 如 asdas123_ 3、右侧随意选择一种选项 如@QQ.com 4.鼠标焦点离开账号输入框" 提示:请以字母或数字结尾 1 V_0020 输入以字母或数字结尾 注册页面 "1、进入邮箱账号申请页面 2、左侧输入以字母或数字结尾 如asdasw_11 3、右侧随意选择一种选项 如@qq.com 4.鼠标焦点离开账号输入框" 提交成功 1 V_0021 显示输入状态 注册页面 "1、进入QQ邮箱账号申请页面 2、鼠标点击昵称输入框" 光标闪动,输入框亮色 1 V_0022 昵称不能为空 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.不输入任何符号 4.鼠标焦点离开昵称输入框" "提示:1.请输入昵称 2.昵称输入框变红" 1 V_0023 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.输入空格符号 4.鼠标焦点离开昵称输入框" "提示:1.昵称不能为空格 2.昵称输入框变红" 1 V_0024 输入大于24位字符 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.输入大于24位的字符 如 aaaaaaaa11111111$$$$$$$$y 4.鼠标焦点离开昵称输入框" 提示:不能超过24个字符或12个汉字 1 V_0025 输入大于12个汉字 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.输入大于12个的汉字 如啊啊啊啊啦啊啊啊啊啦啊啊啊 4.鼠标焦点离开昵称输入框" 提示:不能超过24个字符或12个汉字 1 V_0026 输入大于12位字符和6个汉字 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.输入大于12位字符和6个汉字 如asdc1234^&*&啊啊啦暗暗额h 4.鼠标焦点离开昵称输入框" 提示:不能超过24个字符或12个汉字 1 V_0027 输入不超过24个字符或12个汉字 注册页面 "1.进入QQ邮箱账号申请页面 2.点击进入昵称输入框 3.输入12342 4.鼠标焦点离开昵称输入框" 提示:创建成功 1 V_0028 密码提示功能 注册页面 "1.进入QQ邮箱账号申请页面 2.鼠标点击密码输入框" 提示:请输入密码 1 V_0029 密码不能为空 注册页面 "1.进入QQ邮箱账号申请页面 2.不输入任何符号 3.鼠标焦点离开密码输入框" "提示:1请输入密码 2密码框变红" 1 V_0030 注册页面 "1.进入QQ邮箱账号申请页面 2.输入空格符号 3.鼠标焦点离开密码输入框" "提示:1密码不能为空格 2密码框变红" 1 V_0031 设置密码软键盘 注册页面 "1.进入QQ邮箱账号申请页面 2.鼠标焦点进入密码输入框 3.旁边出现软键盘图标 4.进入软键盘,看能否使用" 弹出软件盘能正常使用 1 V_0032 填写不合理密码长度小于6位字符 注册页面 "1.进入QQ邮箱账号申请页面 2.输入小于6位的字符,如55555 3.鼠标焦点离开密码输入框" "提示:1密码长度应为6-16位字符 2密码框变红" 1 V_0033 填写不合理密码长度大于16位字符 注册页面 "1.进入QQ邮箱账号申请页面 2.输入大于16位的字符,如55555666667777788 3.鼠标焦点离开密码输入框" "提示:1密码长度不能过长 2密码框变红" 1 V_0034 "密码不能为9位以下的纯数字 " 注册页面 "1.进入QQ邮箱账号申请页面 2.输入小于9位的纯数字,如88888888 3.鼠标焦点离开密码输入框" "提示:1密码不能为9位以下的纯数字 2密码框变红" 1 V_0035 "填写合理的密码最小长度 区分大小写" 注册页面 "1.进入QQ邮箱账号申请页面 2.输入6位的字符,如z55555 3.鼠标焦点离开密码输入框" 提示:密码成功 1 V_0036 "填写合理的密码最大长度 区分大小写" 注册页面 "1.进入QQ邮箱账号申请页面 2.输入16位的字符,如5555566666777778 3.鼠标焦点离开密码输入框" 提示:密码成功 1 V_0037 "填写合理的密码纯数字 " 注册页面 "1.进入QQ邮箱账号申请页面 2.输入9位的纯数字,如888888888 3.鼠标焦点离开密码输入框" 提示:密码成功 1 V_0038 "填写合理的密码组合数字加字母 区分大小写" 注册页面 "1.进入QQ邮箱账号申请页面 2.输入数字加组合,如8888zasd 3.鼠标焦点离开密码输入框" 提示:密码成功 1 V_0039 提示密码强度 弱 注册页面 "1.进入QQ邮箱账号申请页面 2.输入纯数字,如8888888888 3.鼠标焦点离开密码输入框" 提示:密码强度 弱 1 V_0040 提示密码强度 中 注册页面 "1.进入Q邮箱Q账号申请页面 2.输入字母加数字,如zxcs7654 3.鼠标焦点离开密码输入框" 提示:密码强度 中 1 V_0041 提示密码强度 强 注册页面 "1.进入QQ邮箱账号申请页面 2.输入数字+字母+符号,如888zsd#¥% 3.鼠标焦点离开密码输入框" 提示:密码强度 强 1 V_0042 确认密码不能为空 注册页面 "1.进入QQ邮箱账号申请页面 2.不输入任何符号 3.鼠标焦点离开密码输入框" 提示:请再次输入密码 1 V_0043 确认密码要一致 注册页面 "1.进入QQ邮箱账号申请页面 2.输入不一致的密码 3.鼠标焦点离开密码输入框" 提示:密码不一致 1 V_0044 注册页面 "1.进入QQ邮箱账号申请页面 2.输入一致的密码 3.鼠标焦点离开密码输入框" 提示:密码成功 1 V_0045 设置确认密码软键盘 注册页面 "1.进入QQ邮箱账号申请页面 2.鼠标焦点进入确认密码输入框 3.旁边出现软键盘图标 4.进入软键盘,看能否使用" 弹出软键盘能正常使用 1 V_0046 选择性别 (默认设置为 男) 注册页面 "1.进入QQ邮箱账号申请页面 2.选择性别 默认设置为 男 3.鼠标焦点离开性别选择输入框" 提示:选择成功 1 V_0047 注册页面 "1.进入QQ邮箱账号申请页面 2.选择性别 如 女 3.鼠标焦点离开性别选择输入框" 提示:选择成功 1 V_0048 不选择生日日期:年 月 日 注册页面 "1.进入QQ邮箱账号申请页面 2.不选择任何选项 3.鼠标焦点离开生日输入框" 提示:请选择生日 1 V_0049 只选择生日日期:年 注册页面 "1.进入QQ邮箱账号申请页面 2.只选择年份,如1991年 3.鼠标焦点离开生日输入框" 提示:请选择生日 1 V_0050 只选择生日日期:年 月 注册页面 "1.进入QQ邮箱账号申请页面 2.只选择年份+月份,如1991年1月 3.鼠标焦点离开生日输入框" 提示:请选择生日 1 V_0051 "选择生日日期:年 月 日 默认设置为 公历" 注册页面 "1.进入QQ邮箱账号申请页面 2.选择年份+月份+日期,默认设置为公历,如 公历 1991年1月11日 3.鼠标焦点离开生日输入框" 提示:属马 魔蝎座 1 V_0052 查看年份下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.进入选择年份 3.查看年份下拉框选项内容是否正确 " 年份选项正确 1 V_0053 输入错误年份 注册页面 "1.进入QQ邮箱账号申请页面 2.进入选择年份 输出错误年份 3.自动默认年份第1个选项为默认值 " 默认年份第1个选项 1 V_0054 查看月份下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出年份 进入选择月份 3.查看月份下拉框选项内容是否正确 " 月份选项正确 1 V_0055 输入错误月份 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出年份 进入选择月份 输出错误月份 3.自动默认月份第1个选项为默认值 " 默认月份第1个选项 1 V_0056 查看日期下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出年份和月份 进入选择日期 3.查看日期下拉框选项内容是否正确 " 日期选项正确 1 V_0057 输入错误日期 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出年份和月份 进入选择日期 输出错误日期 3.自动默认日期第1个选项为默认值 " 默认日期第1个选项 1 V_0058 生日是否有农历选项 注册页面 "1.进入QQ邮箱账号申请页面 2.在公历下拉框选项内容 3.选择出农历 选项值" 能选择出农历选项 1 V_0059 查看农历年份下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.进入农历选项 选择年份 3.查看年份下拉框选项内容是否正确 " 年份选项正确 1 V_0060 查看农历月份下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.进入农历选项 选择出年份 再选择月份 3.查看月份下拉框选项内容是否正确 " 月份选项正确 1 V_0061 查看农历日期下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.进入农历选项 选择出年份和月份 选择日期 3.查看日期下拉框选项内容是否正确 " 日期选项正确 1 V_0062 选择所在地 注册页面 "1.进入QQ邮箱账号申请页面 2.选择你的所在地 默认设置为 中国广东省广州市 3.鼠标焦点离开所在地输入框" 提示:选择成功 1 V_0063 查看国家所在地下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.进入选择国家 3.查看国家下拉框选项内容是否正确 " 国家选项正确 1 V_0064 输入错误国家 注册页面 "1.进入QQ邮箱账号申请页面 2.进入选择国家 输出错误国家 3.自动默认国家第1个选项为默认值 " 默认国家第1个选项 1 V_0065 查看省份下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出国家 进入选择省份 3.查看省份下拉框选项内容是否正确 " 省份选项正确 1 V_0066 输入错误省份 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出国家 进入选择省份 输出错误省份 3.自动默认省份第1个选项为默认值 " 默认省份第1个选项 1 V_0067 查看城市下拉框选项内容是否正确 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出国家和省份 进入选择城市 3.查看城市下拉框选项内容是否正确 " 城市选项正确 1 V_0068 输入错误城市 注册页面 "1.进入QQ邮箱账号申请页面 2.选择出国家和省份 进入选择城市 输出错误城市 3.自动默认城市第1个选项为默认值 " 默认城市第1个选项 1 V_0069 填写不合理的验证码 注册页面 "1.进入QQ邮箱账号申请页面 2.不输入任何符号 3.鼠标焦点离开验证码输入框" 提示:请输入验证码 1 V_0070 注册页面 "1.进入QQ邮箱账号申请页面 2.输入不一致的验证码 3.鼠标焦点离开验证码输入框" "提示:输入错误 请重新输入" 1 V_0071 填写合理的验证码 不区分大小写 注册页面 "1.进入QQ邮箱账号申请页面 2.输入正确的验证码 3.鼠标焦点离开验证码输入框" 提示:选择成功 1 V_0072 验证码的图片可否换一张 注册页面 "1.进入QQ邮箱账号申请页面 2.点击验证码图片的旁边“换一张”看能否更换" 更换成功 1 V_0073 选择“我已阅读并同意相关服务条款” 注册页面 "1.进入QQ邮箱账号申请页面 2.不选择“我已阅读并同意相关服务条款” 3.鼠标焦点离开输入框" "提示:1.请选择“我已阅读并同意相关服务条款” 2.立即注册选项变黑" 1 V_0074 注册页面 "1.进入QQ邮箱账号申请页面 2.选择“我已阅读并同意相关服务条款” 3.鼠标焦点离开输入框" 提示:选择成功 1 V_0075 能否点击查看《QQ号码规则》 注册页面 "1.进入QQ邮箱账号申请页面 2.点击“我已阅读并同意相关服务条款”旁边的选项下拉框进入《QQ号码规则》 3.是否可以进入《QQ号码规则》" 可以进入 1
pytest ui自动化测试实战主要包括安装并配置环境、练习pytest基本使用、练习selenium基本使用以及使用pytest和selenium实现UI自动化测试四个部分。在练习pytest基本使用中,我们可以通过编写简单的用例格式来实现对测试目的和要求的覆盖。而在练习selenium基本使用中,我们可以使用脚本语言来模拟用户行为操作,接近真实用户场景,实现对web页面的自动化测试。通过这些实践,我们可以了解基于pytest和selenium的UI自动化测试的基本思路,并熟悉这两种测试工具的基本操作。此外,pytest还是一个非常成熟的Python测试框架,它具有简单的用例编写格式和丰富的插件,如pytest-html和pytest-rerunfailures等,可以生成漂亮的测试报告并实现失败用例的重复执行。通过实战,我们可以基本掌握pytest的使用方法。另外,还可以基于pytest实现测试用例收集方案、自定义参数化方案、页面元素定位数据存储方案、测试用例数据存储和维护方案等,以避免重复设计和维护复杂的问题。此外,还可以修改定制并汉化html测试报告,使其更加简洁、美观、易读。同时,还可以封装集成selenium、appium、minium和WinAppDriver等工具,以更好地支持不同平台和应用的自动化测试。总之,通过pytest ui自动化测试实战,我们可以全面了解和掌握UI自动化测试的基本原理和实践技巧。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [软件测试案例|使用 pytest+selenium 进行UI自动化测试](https://blog.csdn.net/qq_41640218/article/details/124031645)[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] - *3* [基于pytest设计自动化测试框架实战](https://blog.csdn.net/hotswwkyo/article/details/103211805)[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、付费专栏及课程。

余额充值