前言
網盜概念^-^相同的測試腳本使用不同的測試數據來執行,測試數據和測試行為完全分離, 這樣的測試腳本設計模式稱為數據驅動。(網盜結束)當我們測試某個網站的登錄功能時,我們往往會使用不同的用戶名和密碼來驗證登錄模塊對系統的影響,那么如果我們每一條數據都編寫一條測試用例,這無疑是增加了代碼量,代碼重復,且顯得那么臃腫(誰不喜歡身材好的呢?你懂的),這時候我們可以使用不同數據驅動代碼執行相同的用例測試不同的場景。
實施數據驅動步驟
我們再來說說實施數據驅動測試的步驟:
1.創建/准備測試數據
2.封裝讀取數據的方法,保留測試腳本調用的接口/屬性(我們需要傳遞給腳本什么參數)
3.編寫自動化測試腳本
4.腳本中調用封裝好的處理數據文件的模塊並引入測試數據
5.執行測試腳本並分析測試結果
數據驅動測試環境准備
1.安裝python3.x開發環境(能看到此文章的應該都有這個環境,沒有的自行百度吧)
2.安裝數據驅動模塊ddt
安裝方式1:cmd下執行命令 pip install ddt
安裝方式2:https://pypi.org/simple/ddt/ 下載 並解壓任意目錄,cmd 運行命令python setup.py install
3.驗證安裝 pycharm 新建python文件並輸入 import ddt 運行無報錯信息既表示安裝成功或者cmd 命令依次輸入python回車 import ddt回車 無保存信息表示安裝成功
4.unittest框架和ddt進行數據驅動
測試步驟
1.訪問地址:https://mail.sohu.com/fe/#/login
2.輸入用戶名和密碼
3.點擊登錄按鈕
4.判斷是否登錄成功
數據存儲
數據存在當前腳本中
數據准備
我們要實現的是用戶登錄的操作,所以用戶名和密碼是必須有的,期望結果可以有也可以沒有。數據類型看源代碼!
實例代碼

1 from selenium import webdriver 2 from ddt import ddt, data, unpack 3 import unittest 4 import time 5 from selenium.common.exceptions import NoSuchWindowException 6 ''' 7 簡單數據驅動測試 8 ''' 9 @ddt 10 class ddtTest(unittest.TestCase): 11 # 數據 可以是元祖, 列表, 字典(可迭代對象) 12 value = [['13691579846@sohu.com', 'xiaochao11520','https://mail.sohu.com/fe/#/homepage'], 13 ['13691579844@sohu.com', 'xiaochao11520','https://mail.sohu.com/fe/#/homepage']] 14 # value = [{'uname':'******@sohu.com', 'password':'xiaochao11520','expected':'https://mail.sohu.com/fe/#/homepage'}, 15 # {'uname':'******@sohu.com', 'password':'xiaochao11520','expected':'https://mail.sohu.com/fe/#/homepage'}] 16 def setUp(self): 17 self.testUrl = 'https://mail.sohu.com/fe/#/login' 18 self.driver = webdriver.Firefox() 19 self.driver.get(self.testUrl) 20 21 @data(*value) # * 解析數據 22 @unpack# 用來解包, 將每組數據的第一個數據傳遞給uname依次類推, 當數據為字典時,形參需和字段的key值相同 23 def test_case1(self, uname, password, expected): 24 try: 25 username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']") 26 username.send_keys(uname) 27 time.sleep(1) 28 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']") 29 userpassword.send_keys(password) 30 self.driver.find_element_by_xpath("//input[@type='submit']").click() 31 time.sleep(2) 32 currenturl = self.driver.current_url 33 self.assertEqual(expected, currenturl,'登錄失敗') 34 except NoSuchWindowException as e: 35 print(e) 36 raise 37 except AssertionError: 38 print('期望值是{}, 實際值是{}'.format(expected,currenturl)) 39 raise 40 except Exception: 41 raise 42 def tearDown(self): 43 self.driver.quit() 44 # pass 45 if __name__ == '__main__': 46 unittest.main()
源碼分析
1.@ddt來裝飾測試類(ddt數據驅動的規范寫法,記住就ok)
2.@data(*value)裝飾測試用例(也是一種規范,這邊又涉及到裝飾器,不懂的可以百度或者看我之前的文章又介紹,這邊不再贅述,一句話兩句話也說不清楚)記住:*value作用是打散數據,比如上面代碼是用一個大列表存儲兩個小列表存放數據的,那么*value會得到兩個小列表,每個小列表是一組測試數據
3.@unpack 解析*value數據,會把兩個小列表里面的每一個數據取出來分別傳遞給我們測試用例的形參
方式1缺點
存儲大量數據時,需查看源代碼,不利於腳本的維護
json文件讀取測試數據進行數據驅動測試
數據准備
新建一個json文件(也可以是txt文件),將我們需要的兩組測試數據以列表的形式寫到json文件中,每組數據的每一項參數用相同的符號分割開(方便腳本讀取數據)
實例代碼

{ "value1":"******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage", "value2":"******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage" }

1 from selenium import webdriver 2 from ddt import ddt, file_data 3 import unittest, time 4 from selenium.common.exceptions import NoSuchWindowException 5 import HTMLTestRunner 6 ''' 7 從文件中讀測試數據 8 ''' 9 10 @ddt # ddt裝飾測試類 11 class Testdata(unittest.TestCase): 12 13 def setUp(self): 14 self.driver = webdriver.Firefox() 15 self.driver.get('https://mail.sohu.com/fe/#/login') 16 17 @file_data('test_data.json') # 讀取文件的 文件中數據可以是一個列表,也可以是一個字典 18 def test_data(self,value): 19 uname, password, expected = tuple(value.strip().split('||')) # value是一個字符串 20 # print(type(value),value) 21 try: 22 username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']") 23 username.send_keys(uname) 24 time.sleep(1) 25 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']") 26 userpassword.send_keys(password) 27 self.driver.find_element_by_xpath("//input[@type='submit']").click() 28 time.sleep(2) 29 currenturl = self.driver.current_url 30 self.assertEqual(expected, currenturl,'登錄失敗') 31 except NoSuchWindowException as e: 32 raise e 33 except AssertionError: 34 print('期望值是{}, 實際值是{}'.format(expected,currenturl)) 35 raise 36 except Exception: 37 raise 38 39 def tearDown(self): 40 self.driver.quit() 41 if __name__ == '__main__': 42 unittest.main() 43 # import os 44 # from datetime import date 45 # currentPath = os.path.dirname(os.path.abspath(__file__))# 獲取當前文件目錄 46 # reportPath = os.path.join(currentPath,'report') # 創建一個report目錄 47 # if not os.path.exists(reportPath): 48 # os.mkdir(reportPath) # 判斷目錄是否存在, 不存在就創建 49 # reportName = os.path.join(reportPath, str(date.today())+'report.html') # 拼接html報告 50 # with open(reportName,'wb') as f: 51 # suite = unittest.TestLoader().loadTestsFromTestCase(Testdata) 52 # runner = HTMLTestRunner.HTMLTestRunner(stream=f,verbosity=1, title='數據驅動測試報告', description='數據驅動') 53 # runner.run(suite)
源碼分析
1.相對上個實例,這里使用了@file_data(文件路徑), 參數必須是一個文件,這里是一個json文件, 數據可以是一個列表,也可以是一個字典
# 列表形式
[
"*******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage", "*******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage" ]
2.測試用例接收的是一個字符串,需要對字符串進行處理,把用戶名,密碼,期望值解析出來
方式2優缺點
測試數據存在文件中,方便管理修改,添加數據,易於維護,缺點呢?emmm個人認為這種方式最好!
從xml讀取數據進行數據驅動測試
數據准備
新建一個xml格式的文件,按照xml格式的語法需求,填寫數據(xml文檔我也不是很懂,簡單的顯示個文字啥的還可以^-^!)

<?xml version="1.0"?> <bookList type="technolog"> <book> <uname>******@sohu.com</uname> <password>xiaochao11520</password> <expected>https://mail.sohu.com/fe/#/homepage</expected> </book> <book> <uname>******@sohu.com</uname> <password>xiaochao11520</password> <expected>https://mail.sohu.com/fe/#/homepage</expected> </book> </bookList>
實例代碼

1 from xml.etree import ElementTree 2 3 class ParseXml(object): 4 def __init__(self, xmlpath): 5 self.xmlpath = xmlpath 6 7 # 獲取根節點 8 def getRoot(self): 9 tree = ElementTree.parse(self.xmlpath) 10 root = tree.getroot() 11 return root 12 13 # 根據根節點查找子節點 14 def findNodeByName(self, parentNode, nodeName): 15 nodes = parentNode.findall(nodeName) 16 return nodes 17 18 def getNodeOfChildText(self, node): 19 # 獲取節點node下所有子節點的節點名作為key 20 # 本節點作為value組成的字典對象 21 childrenTextDict = {} 22 for i in list(node.iter())[1:]: # node 節點下的所有節點組成的列表 23 childrenTextDict[i.tag] = i.text 24 # print(list(node.iter())[1:]) 25 return childrenTextDict 26 27 # 獲取節點node下面的節點的所有數據 28 def getDataFromXml(self, node): 29 root = self.getRoot() 30 books = self.findNodeByName(root, node) 31 dataList=[] 32 for book in books: 33 childrentext = self.getNodeOfChildText(book) 34 dataList.append(childrentext) 35 return dataList 36 if __name__=='__main__': 37 xml = ParseXml('./xmlData.xml') 38 root = xml.getRoot() 39 print(root.tag) 40 books = xml.findNodeByName(root, 'book') # 查找所有的book節點 41 for book in books: 42 # print(book[0].tag, book[0].text) 43 print(xml.getNodeOfChildText(book)) 44 print(xml.getDataFromXml('book'))

1 from dataDdt.doXML import ParseXml 2 from selenium import webdriver 3 from selenium.common.exceptions import NoSuchWindowException, TimeoutException 4 import unittest 5 from ddt import ddt, data,unpack 6 import time 7 from selenium.webdriver.support import expected_conditions as EC 8 from selenium.webdriver.support.ui import WebDriverWait 9 from selenium.webdriver.common.by import By 10 values = ParseXml('./xmlData.xml') 11 @ddt 12 class xmltest(unittest.TestCase): 13 14 def setUp(self): 15 self.driver = webdriver.Firefox() 16 self.driver.get('https://mail.sohu.com/fe/#/login') 17 @data(*values.getDataFromXml('book')) 18 @unpack 19 def test_xml(self,uname, password, expected): 20 try: 21 wait = WebDriverWait(self.driver,5) 22 wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@type='submit']"))) 23 username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']") 24 username.send_keys(uname) 25 time.sleep(1) 26 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']") 27 userpassword.send_keys(password) 28 self.driver.find_element_by_xpath("//input[@type='submit']").click() 29 time.sleep(2) 30 currenturl = self.driver.current_url 31 self.assertEqual(expected, currenturl, '登錄失敗') 32 except TimeoutException as e: 33 raise e 34 except NoSuchWindowException as e: 35 raise e 36 except AssertionError as e: 37 print('期望值是{}, 實際值是{}'.format(expected, currenturl)) 38 raise e 39 except Exception: 40 raise 41 def tearDown(self): 42 self.driver.quit() 43 44 if __name__=='__main__': 45 unittest.main()
源碼分析
1.xml文檔編寫(深入了解需百度)有點像html,但又有不同,xml中的節點可以是任意名稱,每個節點同樣是成雙出現
2.增加了doXML.py文檔,用來解析xml文件,方便腳本獲取數據(注釋寫的很詳細,不懂的化可以慢慢調試,哪里不懂print哪里)
3.測試腳本和上面的實例大致相同(不懂的加我qq直接問)
方式3優缺點
優點是做到了數據與測試的分離,方便數據維護,缺點也比較明顯,需要對xml文檔有一定的了解
總結
上面的數據驅動測試步驟是我自己總結的,看了上面的實例對於步驟應該還算合理,下面是我在網上找到的數據驅動測試步驟(感覺比較官方!大家可以參考)
1.編寫測試腳本, 腳本需要支持從程序對象, 文件,或者數據庫讀入數據。(個人觀點:如果腳本先編寫完,測試數據還未准備,后期還要做修改)
2.將測試腳本使用的測試數據存入程序對象,文件,或者數據庫等外部介質中。(個人觀點:這個階段實為准備數據的階段,也就是我們數據要存在哪里,理應放在第一步)
3.運行腳本過程中,循環調用存儲在外部介質中的測試數據。(個人觀點:這里要考慮我們如何讀取,使用數據)
4.驗證所有的測試結果是否符合預期結果
ps:源碼已經上傳到我的git,地址:https://github.com/13691579846/WebdriverAPI 歡迎交流討論QQ交流群:878565760