Pytest+Selenium UI自动化测试实战实例(全)

🍅 视频学习:文末有免费的配套视频可观看

🍅 关注公众号【互联网杂货铺】,回复 1 ,免费获取软件测试全套资料,资料在手,涨薪更快

今天来说说pytest吧,经过几周的时间学习,有收获也有疑惑,总之最后还是搞个小项目出来证明自己的努力不没有白费

环境准备

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 PytestAutoTestFrameWork2 |—|config3 |——|__init__.py4 |——|conf.py5 |——|config.ini6 |—|data7 |——|__init__.py8 |——|tcData.xlsx9 |—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邮箱测试范围分析,我们需要通过设计剪切板,模拟键盘完成附件上传操作,因此我们首先来编写这两个方法
clipboard.py-操作剪切板

 1 """2 ------------------------------------3 @Time : 2019/4/15 12:044 @Auth : linux超5 @File : clipboard.py6 @IDE  : PyCharm7 @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'))

keyboard.py-模拟键盘

 1 """2 ------------------------------------3 @Time : 2019/4/15 12:054 @Auth : linux超5 @File : keyboard.py6 @IDE  : PyCharm7 @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对象库和测试数据文件
parseExcelFile.py-解析Excel文件

 1 """2 ------------------------------------3 @Time : 2019/4/22 16:124 @Auth : linux超5 @File : parseExcelFile.py6 @IDE  : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """10 from openpyxl import load_workbook11 from config.conf import excelPath12 13 14 class ParseExcel(object):15 16     def __init__(self):17         self.wk = load_workbook(excelPath)18         self.excelFile = excelPath19 20     def getSheetByName(self, sheetName):21         """获取sheet对象"""22         sheet = self.wk[sheetName]23         return sheet24 25     def getRowNum(self, sheet):26         """获取有效数据的最大行号"""27         return sheet.max_row28 29     def getColsNum(self, sheet):30         """获取有效数据的最大列号"""31         return sheet.max_column32 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).value39             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).value50             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).value58         if value is None:59             value = ''60         return value61 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).value71                 if value is None:72                     value = ''73                 rowValues.append(value)74             allValues.append(tuple(rowValues))75         return allValues76 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))

parseConFile.py-解析配置文件

1 """2 ------------------------------------3 @Time : 2019/4/18 10:544 @Auth : linux超5 @File : parseConFile.py6 @IDE  : PyCharm7 @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'))

config.ini

1 [126LoginAccount];126邮箱正确的登录账号和密码;运行用例时请更换正确的用户名和密码2 username=linuxxiaochao3 password=xiaochao115204 [HomePageElements];126邮箱首页菜单栏元素5 homePage=id->_mail_tabitem_0_3text6 mailList=id->_mail_tabitem_1_4text7 applicationCenter=id->_mail_tabitem_2_5text8 inBox=id->_mail_tabitem_3_6text9 [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邮箱的功能,以便后续设计用例调用
BasePage.py-webdriver二次封装

 1 """2 ------------------------------------3 @Time : 2019/4/20 8:454 @Auth : linux超5 @File : BasePage.py6 @IDE  : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """10 import time11 from selenium.webdriver.support import expected_conditions as EC12 from selenium.webdriver.support.wait import WebDriverWait as wd13 from selenium.webdriver.common.by import By14 from selenium.common.exceptions import NoSuchWindowException, TimeoutException, \15     NoAlertPresentException, NoSuchFrameException16 from selenium import webdriver17 18 from util.clipboard import ClipBoard19 from util.keyboard import KeyBoard20 from util.parseConFile import ParseConFile21 from util.parseExcelFile import ParseExcel22 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_TEXT38         }39         self.driver = driver40         self.outTime = outTime41 42     def findElement(self, by, locator):43         """44         find alone element45         :param by: eg: id, name, xpath, css.....46         :param locator: id, name, xpath for str47         :return: element object48         """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 e58         else:59             # print('[Info:Had found the element "{}" by "{}"!]'.format(locator, by))60             return element61 62     def findElements(self, by, locator):63         """64         find group elements65         :param by: eg: id, name, xpath, css.....66         :param locator: eg: id, name, xpath for str67         :return: elements object68         """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 e78         else:79             # print('[Info:Had found the elements "{}" by "{}"!]'.format(locator, by))80             return elements81 82     def isElementExsit(self, by, locator):83         """84         assert element if exist85         :param by: eg: id, name, xpath, css.....86         :param locator: eg: id, name, xpath for str87         :return: if element return True else return false88         """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 False96             except NoSuchWindowException:97                 print('Error: element "{}" not exsit!'.format(locator))98                 return False99             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"]')

HomePage.py-邮箱首页选择菜单

 1 """2 ------------------------------------3 @Time : 2019/4/20 12:284 @Auth : linux超5 @File : HomePage.py6 @IDE  : PyCharm7 @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()

LoginPage.py-封装登录功能

1 """2 ------------------------------------3 @Time : 2019/4/20 12:284 @Auth : linux超5 @File : LoginPage.py6 @IDE  : PyCharm7 @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('请输入密码')

ContactPage.py-封装添加联系人功能

 1 """2 ------------------------------------3 @Time : 2019/4/20 12:294 @Auth : linux超5 @File : ContactPage.py6 @IDE  : PyCharm7 @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')

SendMailPage.py-封装发送邮件功能

 1 """2 ------------------------------------3 @Time : 2019/4/20 9:164 @Auth : linux超5 @File : SendMailPage.py6 @IDE  : PyCharm7 @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文件并调用登录功能(为什么这么做,不明白的小伙伴可以去看一下我之前的文章,关于conftest.py的原理)
conftest.py-同用例目录下,调用登录功能

 1 """2 ------------------------------------3 @Time : 2019/4/20 15:104 @Auth : linux超5 @File : conftest.py6 @IDE  : PyCharm7 @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,开始编写测试用例啦
test_loginCase.py-登录功能测试

 1 """2 ------------------------------------3 @Time : 2019/4/20 14:104 @Auth : linux超5 @File : test_loginCase.py6 @IDE  : PyCharm7 @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'])

test_contactCase.py-添加联系人功能测试

 1 """2 ------------------------------------3 @Time : 2019/4/20 16:154 @Auth : linux超5 @File : test_contactCase.py6 @IDE  : PyCharm7 @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'])

test_sendMailCase.py-发送邮件功能测试

 1 """2 ------------------------------------3 @Time : 2019/4/20 10:044 @Auth : linux超5 @File : test_sendMailCase.py6 @IDE  : PyCharm7 @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生成的,但是这样的报告对用例的描述不是很清晰,且没有对失败用例截图,也不方便我们分析项目的缺陷,我们也可以填写代码放到这个文件里面
conftest.py-全局conftest.py文件

 1 """2 ------------------------------------3 @Time : 2019/4/12 14:104 @Auth : linux超5 @File : conftest.py6 @IDE  : PyCharm7 @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()

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

 1 """2 ------------------------------------3 @Time : 2019/4/20 16:504 @Auth : linux超5 @File : conf.py6 @IDE  : PyCharm7 @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模块自动执行相关命令,编写运行用例代码
RunTestCase.py-执行用例文件

 1 """2 ------------------------------------3 @Time : 2019/4/15 16:144 @Auth : linux超5 @File : RunTestCase.py6 @IDE  : PyCharm7 @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配置文件了

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

pytest.ini-pytest配置文件

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.自动发送测试邮件给指定用户

图片

同时,在这我为大家准备了一份软件测试视频教程(含面试、接口、自动化、性能测试等),就在下方,需要的可以直接去观看,也可以直接【点击文末小卡片免费领取资料文档】

7天Python自动化测试速成课,小白也能快速上手(项目实战)

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

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

相关文章

Redis实现用户活跃排行榜

在这里用户活跃度排行榜&#xff0c;主要是基于redis的zset数据结构来实现的&#xff0c;下面来看一下实例。 方案设计 来看一下业务场景先 1.场景说明 在技术派中&#xff0c;提供了一个用户的活跃排行榜&#xff0c;当然作为一个博客社区&#xff0c;更应该实现的是作者排…

连号区间数 刷题笔记

1.单个元素算一个连续区间 2.题意为 单独截取数组中的一段元素 例如 a数组假设为 3 1 2 5 4 6 7 假设取 a[3]-a[5] 则取出 5 4 6 重新排序后为 4 5 6 连续了 则ans; 假设 取a[i]-a[j]这一段元素 设该段元素的最大值为max,最小值为min 如果该段元素重新排序后…

Learn OpenGL 04 纹理

纹理环绕方式 纹理坐标的范围通常是从(0, 0)到(1, 1)&#xff0c;那如果我们把纹理坐标设置在范围之外会发生什么&#xff1f;OpenGL默认的行为是重复这个纹理图像&#xff08;我们基本上忽略浮点纹理坐标的整数部分&#xff09;&#xff0c;但OpenGL提供了更多的选择&#xf…

LCR 131. 砍竹子 I

解题思路&#xff1a;&#xff08;与砍竹子II的区别是&#xff0c;这里的竹子长度数量级较小&#xff09; 数学推导或贪心 切分规则&#xff1a; 等长&#xff0c;且尽量为3 b0时&#xff0c;pow(3,a) b1时&#xff0c;pow(3,a-1)*4 少一段3&#xff0c;并入b生成一…

YUNBEE云贝:3月9日-PostgreSQL中级工程师PGCE认证培训

课程介绍 根据学员建议和市场需求,规划和设计了《PostgreSQL CE 认证课程》,本课程以内部原理、实践实战为主&#xff0c;理论与实践相结合。课程包含PG 简介、安装使用、服务管理、体系结构等基础知识。同时结合一线实战案例&#xff0c; 面向 PG 数据库的日常维护管理、服务和…

Vue | 基于 vue-admin-template 项目的跨域问题解决方法

目录 一、现存问题 二、解决方法 2.1 修改的第一个地方 2.2 修改的第二个地方 2.3 修改的第三个地方 自存 一、现存问题 报错截图如下&#xff1a; 二、解决方法 2.1 修改的第一个地方 在 .env.development 文件中&#xff1a; # base api # VUE_APP_BASE_API /d…

springboot整合shiro的实战教程(一)

文章目录 1.权限的管理1.1 什么是权限管理1.2 什么是身份认证1.3 什么是授权 2.什么是shiro3.shiro的核心架构3.1 Subject3.2 SecurityManager3.3 Authenticator3.4 Authorizer3.5 Realm3.6 SessionManager3.7 SessionDAO3.8 CacheManager3.9 Cryptography 4. shiro中的认证4.1…

我的 4096 创作纪念日

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

YOLOv8+DeepSort/ByteTrack-PyQt-GUI / yolov5 deepsort 行人/车辆(检测 +计数+跟踪+测距+测速)

YoloV8结合可视化界面和GUI&#xff0c;实现了交互式目标检测与跟踪&#xff0c;为用户提供了一体化的视觉分析解决方案。通过YoloV8算法&#xff0c;该系统能够高效准确地检测各类目标&#xff0c;并实时跟踪它们的运动轨迹。 用户可以通过直观的可视化界面进行操作&#xff…

Unity性能优化篇(七) UI优化注意事项以及使用Sprite Atlas打包精灵图集

UI优化注意事项 1.尽量避免使用IMGUI(OnGUI)来做游戏时的UI&#xff0c;因为IMGUI的开销比较大。 2.如果一个UGUI的控件不需要进行射线检测&#xff0c;则可以取消勾选Raycast Target 3.尽量避免使用完全透明的图片和UI控件。因为即使完全透明&#xff0c;我们看不见它&#xf…

常见BUG如何在测试过程中分析定位

前言 在测试的日常工作中&#xff0c;相信经常有测试的小伙伴遇到类似的情况&#xff1a;在项目上线时&#xff0c;只要出现问题&#xff08;bug&#xff09;&#xff0c;就很容易成为“背锅侠”。 软件测试人员在工作中是无法避免的要和开发人员和产品经理打交道的&#xff…

117.龙芯2k1000-pmon(16)- linux下升级pmon

pmon的升级总是有些不方便&#xff0c;至少是要借助串口和串口工具 如果现场不方便连接串口&#xff0c;是不是可以使用网线升级pmon呢&#xff1f; 答案当然是可行的。 环境&#xff1a;2k1000linux3.10麒麟的文件系统 如今我已经把这个工具开发出来了。 GitHub - zhaozhi…

网络工程师笔记10 ( RIP / OSPF协议 )

RIP 学习路由信息的时候需要配认证 RIP规定超过15跳认定网络不可达 链路状态路由协议-OSPF 1. 产生lsa 2. 生成LSDB数据库 3. 进行spf算法&#xff0c;生成最有最短路径 4. 得出路由表

【探索C++容器:set和map的使用】

[本节目标] 1. 关联式容器 2. 键值对 3. 树形结构的关联式容器 1. 关联式容器 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为…

Toyota Programming Contest 2024#3(AtCoder Beginner Contest 344)(A~C)

A - Spoiler 竖线里面的不要输出&#xff0c;竖线只有一对&#xff0c;且出现一次。 #include <bits/stdc.h> //#define int long long #define per(i,j,k) for(int (i)(j);(i)<(k);(i)) #define rep(i,j,k) for(int (i)(j);(i)>(k);--(i)) #define debug(a) cou…

链表|面试题 02.07.链表相交

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *l NULL, *s NULL;int lenA 0, lenB 0, gap 0;// 求出两个链表的长度s headA;while (s) {lenA ;s s->next;}s headB;while (s) {lenB ;s s->next;}// 求出两个链表长度差if (lenA &…

stm32学习笔记:SPI通信协议原理(未完)

一、SPI简介(serial Peripheral Interface&#xff08;串行 外设 接口&#xff09;) 1、电路模式&#xff08;采用一主多从的模式&#xff09;、同步&#xff0c;全双工 1 所有SPI设备的SCK、MOSI、MISO分别连在一起 2 主机另外引出多条SS控制线&#xff0c;分别接到各从机的S…

DetNet论文速读

paper&#xff1a;DetNet: A Backbone network for Object Detection 存在的问题 最近的目标检测模型通常依赖于在ImageNet分类数据集上预训练的骨干网络。由于ImageNet的分类任务不同于目标检测&#xff0c;后者不仅需要识别对象的类别&#xff0c;而且需要对边界框进行空间…

音视频开发_音频基础知识

如何采集声音——模数转换原理 声音模数转换是将声音信号从模拟形式转换为数字形式的过程。它是数字声音处理的基础&#xff0c;常用于语音识别、音频编码等应用中。 音视频通信流程 音视频采集&#xff1a;首先是从麦克风、摄像头等设备中采集音频和视频数据&#xff0c;将现…

【Windows】VMware虚拟机应用(一):下载安装 VMware Workstation

目录 一、下载 二、注意事项 三、安装 四、密钥激活 4.1 密钥 4.2 激活 一、下载 进入官网下载页 VMware Customer Connect | The All-In-One VMware Product Support Portal 先登录&#xff0c;下载时要求登录。 点【Downloads】 进入产品下载页面&#xff0c;切换到…