Web應用包含超文本標記語言(HTML)、層疊樣式表演(CSS)、JavaScript腳本的Web頁面。
HTML表單由不同類型的元素組成的,包含<form>、<input>、<button>、<label>
1 webdriver的API與定位元素
from selenium import webdriver b = webdriver.Firefox() b.get('http://www.baidu.com') #輸入url print(b.title) #打印元素 print(b.current_url) #打印url
Webdriver常用方法:

1.1 8種元素定位的方法
| 元素名稱 | webdriver API | 說明 |
| id | find_element_by_id() | |
| name | find_element_by_name() | |
| class name | find_element_by_class_name() | |
| tag name | find_element_by_tag_name() | |
| link text | find_element_by_link_text() | |
| partial link text | find_element_by_partial_link_text() | |
| xpath | find_element_by_xpath() | |
| css selector | find_element_by_css_selector() |
>>> ele = b.find_element_by_id('kw') >>> ele1=b.find_element_by_name('wd')
1.2 元素操作方式
| 方法 | 說明 |
| clear | 清除元素內容 |
| send_keys | 模擬按鍵輸入 |
| click | 點擊 |
| submit | 提交表單 |
>>> ele.clear() >>> ele.send_keys('自動化測試') >>> b.back() #退回 >>> ele1.send_keys('測試')
>>> ele2= b.find_element_by_class_name('s_ipt') >>> ele2.send_keys('selenium') >>> ele3=b.find_element_by_tag_name('input') >>> ele3.size {'height': 0.0, 'width': 0.0} >>> ele3.id '3e2e100e-b754-4ce1-b1d7-7872079247da' >>> ele2.id '2f622532-63aa-4018-8a4c-683f382ae01a' #id不同,右鍵查看頁面源代碼,有其他input
WebElement功能列表:
| 功能/屬性 | 描述 | 實例 |
| size | 獲取元素的大小 | element.size |
| tag_name | 獲取元素的HTML標簽名稱 | element.tag_name |
| text | 獲取元素的文本值 | element.text |
>>> b.get('http://www.dji.com') >>> b.maximize_window() #最大化窗口 >>> ele=b.find_element_by_link_text('消費級產品') >>> ele.click() >>> ele1=b.find_element_by_partial_link_text('消費級') >>> ele1.click() >>> ele_css=b.find_element_by_css_selector('html.js.no-touch.csstransforms3d.csstransitions body.dji-zh-CN.dji-pc nav#site-header.dui-navbar.site-header.collapsed div.navbar-container div#siteHeaderNavbar ul.navbar-category li.category-item a.ga-data') #審查元素后復制css路徑 >>> ele_css=b.find_element_by_css_selector('input[class=\'search-input\']') #可以定位任一元素 >>> ele_css.send_keys('社區')
如果沒有元素與之匹配,則拋出NoSucnElementException異常。
其他WebElement常用方法:

1.3 xpath定位
- xml路徑語言:用來確定xml文檔中某部分位置的語言;
- xpath用於在xml文檔中通過元素和屬性進行導航;
- xpath是一個W3C標准;
- 對xml/html有一定的了解。
xpah節點類型:元素、屬性、文本、命名空間、指令處理、注釋及文檔。
xpath:通過路徑表達式從xml文檔中選取節點或節點設置
| 表達式 | 結果 |
| /xxx | 選取根節點xx |
| /xxx/yyy | 根據絕對路徑選擇元素 |
| //xxx | 整個文檔掃描,找到所有xxx元素 |
| //xxx/yyy | 所有父元素為xxx的yyy元素 |
| . | 選取當前節點的父元素節點 |
| .. | 選取父元素地址 |
| //xxx[@id] | 選取所有xxx元素中有id屬性的元素 |
| //xxx[@id=yyy] | 選取所有xxx元素id屬性為yyy的元素 |
b.get(r'C:\Users\zhouxy\Desktop\bookmark.html') ele = b.find_element_by_xpath('/html') #絕對路徑 ele.text ele = b.find_element_by_xpath('/html/body/form/input') #默認第一個輸入框 ele.get_attribute('type') #-->‘text’ #第二個輸入框 ele1=b.find_element_by_xpath('/html/body/from/input[2]') #並通過input下標找到 e= find.element_by_xpath('//input') #相對路徑 e.get_attribute('name') ele = b.find_element_by_xpath('//input/..') ele.tag_name #-->form 上一級目錄 e = b.find_element_by_xpath('//input[@id]') #'//input[@name="xxx"]'
#遍歷所有
ele= b.find_element_by_xpath('//*') #-->html
#運用函數
ele= b.find_element_by_xpath('//*[count[input]=2]') #-->form
#從表單開始定位
ele= b.find_element_by_xpath('//form[@id=“form”/span/input]')
#組合定位
ele= b.find_element_by_xpath('//input[@name="wd"] and @id="kw"]')
| 表達式 | 結果 |
| //*[count(xxx)=2] | 統計xxx元素個數=2的節點 |
| //*[local-name()='xxx'] | 找到tag為xxx的元素 |
| //*[starts-with(local_name(),'x')] | 找到所有tag以x開頭的元素 |
| //*[contain(local_name())=3] | 找到所有tag包含x的元素 |
| //*[string-length(local-name())=3] | 找到所有tag長度為3的元素 |
| //xxx | //yyy | 多個路徑查找 |
ele1 = b.find_element_by_xpath('//*[local-name()="input"]') #相當於('//input'),默認取第一個input ele2 = b.find_element_by_xpath('//*[start-with(local-name(),"i")]') ele3 = b.find_element_by_xpath('//*[contain(local-name(),"i")]') ele4 = b.find_element_by_xpath('//*[contain(local-name(),"i")]') ele5 = b.find_element_by_xpath('//*[contain(local-name(),"i")][last()]') #包含i的tag_name倒數第一個元素 ele5 = b.find_element_by_xpath('//*[contain(local-name(),"i")][last()]-1') #包含i目錄倒數第二個元素 ele.get_attribute('name') #獲取元素的屬性 ele6 = b.find_element_by_xpath('//*[string-length(local-name())=3]') #長度等於3的tag_name ele7=b.find_element_by_xpath('//title | // input[last()]') #-->title
1.4 css selector定位
| 選擇器 | 描述 | 舉例 |
| * | 通配選擇器,選擇所有的元素 | * |
| <type> | 選擇特定類型的元素,支持基本HTML標簽 | h1 |
| .<class> | 選擇具有特定class的元素。 | .class1 |
| <type>.<class> | 特定類型和特定class的交集。(直接將多個選擇器連着一起表示交集) | h1.class1 |
| #<id> | 選擇具有特定id屬性值的元素 | #id1 |
e= find_element_by_css_selector(".s_ipt") e= find_element_by_css_selector("#kw")
通過屬性選擇器定位:
| 選擇器 | 描述 | 舉例 |
| [attr] | 選取定義attr屬性的元素,即使該屬性沒有值 | [placeholder] |
| [attr="val"] | 選取attr屬性等於val的元素 | [placeholder="請輸入關鍵詞"] |
| [attr^="val"] | 選取attr屬性開頭為val的元素 | [placeholder^="請輸入"] |
| [attr$="val"] | 選取attr屬性結尾為val的元素 | [placeholder$="關鍵詞"] |
| [attr*="val"] | 選取attr屬性包含val的元素 | [placeholder*="入關"] |
| [attr~="val"] | 選取attr屬性包含多個空格分隔的屬性,其中一個等於val的元素 | [placeholder~="關鍵詞"] |
| [attr|="val"] | 選取attr屬性等於val的元素或第一個屬性值等於val的元素 | [placeholder|="關鍵詞"] |
2 鼠標和鍵盤事件
2.1 AcitonChains類與輸入事件
- from selenium.webdriver.common.action_chains import AcitonChains
- ActionCharis(driver):用於生成模擬用戶行為
- perform():執行存儲行為
2.2 鼠標事件
| 表達式 | 說明 |
| context_click | 右擊事件 |
| double_click | 雙擊事件 |
| drag_and_drop | 拖動 |
| move_to_element() | 鼠標停在一個元素上 |
| click_and_hold | 按下鼠標左鍵在一個元素上 |
>>> from selenium import webdriver >>> b = webdriver.Firefox() >>> b.get('http://www.dji.com') >>> from selenium.webdriver.common.action_chains import ActionChains >>> ele=b.find_element_by_link_text('消費級產品') >>> ActionChains(b).move_to_element(ele).perform() >>> sub_ele=b.find_element_by_link_text('御 Mavic Air') >>> sub_ele.click()

2.3 鍵盤事件:send_keys()
from selenium.webdriver.common keys import Keys
| 表達式 | 說明 |
| send_keys(Keys.BACKSPACE) | 退格鍵 |
| send_keys(Keys.CONTRL,'a') | 全選 |
| send_keys(Keys.CONTRL,'v') | 粘貼 |
| send_keys(Keys.CONTRL,'c') | 復制 |
| send_keys(Keys.CONTRL,'x') | 剪切 |
| send_keys(Keys.ENTER') | 回車 |
>>> s =b.find_element_by_name('q') >>> s.sent_keys('大疆') >>> s.clear() >>> s.send_keys('大疆啊') >>> from selenium.webdriver.common.keys import Keys >>> s.send_keys(Keys.BACKSPACE) >>> s.send_keys(Keys.CONTROL,'a') >>> s.send_keys(Keys.CONTROL,'x') >>> s.send_keys(Keys.CONTROL,'v') >>> ele=b.find_element_by_link_text('大疆司空') >>> s.send_keys(Keys.ENTER)
3 對話框與多窗口管理
>>> d = webdriver.Firefox() >>> d.get('http://www.baidu.com') >>> d.find_element_by_id('kw').clear() >>> d.find_element_by_id('kw').send_keys('大疆科技') >>> d.find_element_by_id('su').click() >>> d.find_element_by_partial_link_text('DJI大疆創新 - 所有產品').click() >>> d.window_handles ['4294967297', '4294967301'] #列出所有的句柄 >>> d.current_window_handle '4294967297' #顯示當前句柄 >>>d.switch_to_window(d.window_handles[0]) #切換句柄 >>> d.close() #關閉tab >>> d.quit() #退出瀏覽器
4 下拉框處理
需要特定的Select類並導入,from selenium.webdriver.support.select import Select

5 alter對話框處理
需要先切換到彈框上的,alert = driver.switch_to_alert()
Alter方法:
| swich_to_alert() | 切到alter,返回一個alter對象 |
| accept | 確認 |
| dismiss | 取消 |
| send_keys() | 有輸入框才能使用,否則報錯 |
>>>alert=b.swich_to_alert
6 測試腳本中的等待方法
6.1 元素等待機制
隱式等待:為了解決由於網絡延遲或利用Ajax動態加載元素所導致的程序響應時間不一致。當一個測試用例執行的時候,WebDriver找不到任意一個元素,將會等待,等待時間超過后,則拋出NoSuchElementException。
顯式等待:可以為腳本設置一些預置或定制化的條件,等待條件滿足后再執行測試。顯示等待可以只作用於有同步需求的測試用例。
WebDriver提供WebDriverWait類和expected_conditions類來實現顯式等待。
| implicitly_wait() | 設置webdriver等待時間,等待時間后報異常 |
| WebDriverWait | 等待條件滿足或者超時后退出 from selenium.webdriver.support.ui import WebDriverWait |
6.2 WebDriverWair方法:
構造函數:def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY)
poll_frequency-->check-->until-->method return Not False
-->not until-->method return False
import time
from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait Url = 'http://10.10.200.86:10004/ccs-web/index.jsp' Account = 'zhouxy' Password = '111' def get_ele_time(driver,times,func): return WebDriverWait(driver,times).until(func) def login_test(): b = webdriver.Firefox() #啟動瀏覽器 b.get(Url) #輸入url b.maximize_window() login_ele = get_ele_time(b,10,lambda b:b.find_element_by_xpath('/html/body/form/ \ table/tbody/tr[3]/td/table/tbody/tr[3]/td/table/tbody/tr[1]/td[7]/img')) #也可以用expected_conditions有定義好的預期等待條件 username_ele = b.find_element_by_id('j_username') #用戶名元素 username_ele.clear() #清空 username_ele.send_keys(Account) #輸入用戶名 password_ele = b.find_element_by_id('j_password') password_ele.clear() password_ele.send_keys(Password) login_ele.click() try : ele = b.find_element_by_link_text('Login fail: 錯誤的憑證') print('登錄失敗') except: print('登錄成功') time.sleep(10) b.close() if __name__ == '__main__': login_test()
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC ''' 10秒鍾等待瀏覽器彈出的對話框,如果出現,就點擊確定按鈕 ''' WebDriverWait(chromedriver,10).until(EC.alert_is_present()).accept()
7 文件上傳
7.1 普通上傳(通過input框)
# -*- coding: utf-8 -*- from selenium import webdriver driver = webdriver.Firefox() driver.get('http://sahitest.com/demo/php/fileUpload.htm') upload = driver.find_element_by_id('file') upload.send_keys('d:\\baidu.py') # send_keys print upload.get_attribute('value') # check value driver.quit()
7.2 flash上傳(非input標簽)
有幾種解決方案:
- autoIT,借助外力,我們去調用其生成的au3或exe文件。
- Python pywin32庫,識別對話框句柄,進而操作
- SendKeys庫
- keybd_event,跟3類似,不過是模擬按鍵,ctrl+a,ctrl+c, ctrl+v…
8 文件下載
對於Firefox,需要設置Profile:
browser.download.dir:指定下載路徑browser.download.folderList:設置成2表示使用自定義下載路徑;設置成0表示下載到桌面;設置成1表示下載到默認路徑browser.download.manager.showWhenStarting:在開始下載時是否顯示下載管理器browser.helperApps.neverAsk.saveToDisk:對所給出文件類型不再彈出框進行詢問
from selenium import webdriver from time import sleep profile = webdriver.FirefoxProfile() profile.set_preference('browser.download.dir', 'd:\\') profile.set_preference('browser.download.folderList', 2) profile.set_preference('browser.download.manager.showWhenStarting', False) profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/zip') driver = webdriver.Firefox(firefox_profile=profile) driver.get('http://sahitest.com/demo/saveAs.htm') driver.find_element_by_xpath('//a[text()="testsaveas.zip"]').click() sleep(3) driver.quit()
9 滾動條
from selenium import webdriver import os dr = webdriver.Firefox() dir = os.path.join('file:///'+os.getcwd()+'\\h1.html') dr.get(dir) dr.implicitly_wait(3) js='document.getElementsByClassName("scroll")[0].scrollTop=0' # 就是這么簡單,修改這個元素的scrollTop就可以 dr.execute_script(js)
document.getElementsByClassName("scroll")[0].scrollHeight # 獲取滾動條高度 document.getElementsByClassName("scroll")[0].scrollWidth # 獲取橫向滾動條寬度 document.getElementsByClassName("scroll")[0].scrollLeft=xxx # 控制橫向滾動條位置
10 自動發送郵件
10.1 SMTP模塊發送郵件
SMTP(Simple Mail Transfer Protocol)即簡單郵件傳輸協議。
import smtplib from email.mime.text import MIMEText from email.header import Header def sent_email(SMTP_host,Username,Password,Content,Subject,Revicer): #1.實例化SMTP smtp = smtplib.SMTP() #2.鏈接郵件服務器 smtp.connect(SMTP_host) #3.配置發送者郵箱密碼 smtp.login(Username,Password) #4.配置發送內容msg msg = MIMEText(Content,'html','utf-8') # 三個參數:第一個為文本內容,第二個置文本格式,第三個設置編碼 msg['Subject']= Header(Subject,'utf-8') #郵件主題 msg['From'] = Username msg['To'] = Revicer #5.配置發送郵箱,接收郵箱,以及發送內容 smtp.sendmail(Username,Revicer,msg.as_string()) #6.關閉郵件服務 smtp.quit() if __name__ == '__main__': sent_email('smtp.163.com','****@163.com','******','<html><h1>你好</h1></html>','郵件主題','****@163.com')
10.2 email模塊
email模塊下有mime包,mime英文全稱為“Multipurpose Internet Mail Extensions”,即多用途互聯網郵件擴展,是目前互聯網電子郵件普遍遵循的郵件技術規范。
該mime包下常用的有三個模塊:text,image,multpart。
- 構造一個郵件對象就是一個
Message對象 - 如果構造一個
MIMEText對象,就表示一個文本郵件對象(如果發送內容為中文,需要選擇“plain”,要不然無法顯示) - 如果構造一個
MIMEImage對象,就表示一個作為附件的圖片 - 要把多個對象組合起來,就用
MIMEMultipart對象 - 而
MIMEBase可以表示任何對象。它們的繼承關系如下:
Message
+- MIMEBase
+- MIMEMultipart
+- MIMENonMultipart
+- MIMEMessage
+- MIMEText
+- MIMEImage
10.2.1 添加普通文本
text = "This is a text\nHere is the link you want:\nhttp:\\www.baidu.com" 2 msg = MINEText(text, 'plain', utf-8)
10.2.2 添加超文本
html = """ <html> <body> <p> Here is the <a href="http://www.baidu.com">link</a> you wanted. </p> </body> </html> """ msg = MIMEText(html,'html', 'utf-8')
10.2.3 添加附件
sendfile = open('D:\\python\\sendfile.txt', 'rb').read() msg = MINEText(sendfile, 'base64', 'utf-8') msg['Content-type'] = 'application/octet-stream' msg['Content-Disposition'] = 'attachment;filename= "文件顯示名字.txt"'
10.2.4 添加圖片
sendimagefile=open(r'D:\pythontest\testimage.png','rb').read() msg = MIMEImage(sendimagefile) msg.add_header('Content-ID','<image1>')
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.header import Header import os def send_file(SMTP_host,from_addr,from_pwd,to_addr,file_addr,subject): smtp = smtplib.SMTP(SMTP_host) smtp.login(from_addr,from_pwd) # 郵件主題/發送者/接受者 msg = MIMEMultipart() msg['Subject']= Header(subject,'utf-8') msg['From'] = from_addr msg['To'] = to_addr # 郵件正文 msg.attach(MIMEText('如附件所示','plain','utf-8')) # 郵件附件 with open(file_addr,'rb') as f : file = f.read() att = MIMEText(file,'base64','utf-8') att['Content-type'] = 'application/octet-stream' att['Content-Dispositon'] = 'attachment;filename="test.html"' #文件名 msg.attach(att) smtp.sendmail(from_addr,to_addr,msg.as_string()) smtp.quit() if __name__ == '__main__': try: dir = os.path.join(os.getcwd()+'//h1.html') send_file('smtp.163.com','****@163.com','******','****@163.com',dir,'主題') print('郵件發送成功') except smtplib.SMTPException: print('Error:無法發送郵件')
10.3 自動發送郵件
import unittest,smtplib from test_mathfunc import TestMathFunc from HTMLTestRunner import HTMLTestRunner import time,os,sys email_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(email_path) from web_test.test_email import send_file def SendReport(report_dir): # 獲取最新報告 case_list = os.listdir(report_dir) #獲取report目錄下所有文件,以列表形式返回 case_list.sort(key=lambda fn:os.path.getatime(report_dir+'\\'+fn)) #對case_list中所有元素按時間從大到小排序 latest_report = os.path.join(report_dir,case_list[-1]) print(latest_report) return latest_report if __name__ == '__main__': suite = unittest.TestSuite() tests = [TestMathFunc('test_add'),TestMathFunc('test_minus'),TestMathFunc('test_divide')] suite.addTests(tests) now = time.strftime('%Y%m%d%H%M%S') with open(now+'HTMLReport.html','wb') as f: runner = HTMLTestRunner(stream=f, title='計算器測試', description = '測試報告', verbosity=2) runner.run(suite) R = SendReport("C:\\Users\\zhouxy\\PycharmProjects\\untitled\\unit_test") try: send_file('smtp.163.com','****@163.com','******','****@163.com',R,'主題') print('郵件發送成功') except smtplib.SMTPException: print('Error:無法發送郵件')
11 測試用例設計



數據設計:字典形式
| key | val |
| url | 打開地址 |
| text_id | 登錄元素 |
| userid/pwdid/loginid | 輸入賬號元素 |
| uname/pwd | 輸入賬號信息 |
| errorid | 檢查錯誤條件 |
練手網站推薦:http://sahitest.com/demo/index.html
資源推薦:https://blog.csdn.net/huilan_same/article/details/52615123
