前言
一個自動化測試框架就是一個集成體系,在這一體系中包含測試功能的函數庫、測試數據源、測試對象識別標准,以及種可重用的模塊。自動化測試框架在發展的過程中經歷了幾個階段,模塊驅動測試、數據驅動測試、對象驅動測試。本章就帶領讀者了解這幾種測試模型
1. 自動化測試模型介紹
自動化測試模型是自動化測試架構的基礎,自動化測試的發展也經歷的不同的階段,不斷有新的模型(概念)被提出,了解和使用這些自動化模型將幫助我們構建一個靈活可維護性的自動化架構
1.1 線性測試
通過錄制或編寫腳本,一個腳本完成一個場景(一組完整功能操作),通過對腳本的回放來進行自動化測試。這是早期進行自動化測試的一種形式;我們之前練習使用webdriver API 所編寫的腳本也是
測試腳本一:
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: cnblog登錄.py
@time: 2018-09-27 9:55
@desc:
'''
import time
from selenium import webdriver
driver = webdriver.Firefox()
#添加智能等待
driver.implicitly_wait(10)
driver.get(r'https://passport.cnblogs.com/user/signin?ReturnUrl=https%3A%2F%2Fwww.cnblogs.com%2F')
#登錄用戶
driver.find_element_by_id('input1').clear()
driver.find_element_by_id('input1').send_keys('abcdd')
driver.find_element_by_id('input2').clear()
driver.find_element_by_id('input2').send_keys('abcdd')
driver.find_element_by_id('signin').click()
time.sleep(5)
#執行具體測試用例操作1
driver.quit()
測試腳本二:
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: cnblog登錄.py
@time: 2018-09-27 9:55
@desc:
'''
import time
from selenium import webdriver
driver = webdriver.Firefox()
#添加智能等待
driver.implicitly_wait(10)
driver.get(r'https://passport.cnblogs.com/user/signin?ReturnUrl=https%3A%2F%2Fwww.cnblogs.com%2F')
#登錄用戶
driver.find_element_by_id('input1').clear()
driver.find_element_by_id('input1').send_keys('abcdd')
driver.find_element_by_id('input2').clear()
driver.find_element_by_id('input2').send_keys('abcdd')
driver.find_element_by_id('signin').click()
time.sleep(5)
#執行具體測試用例操作1
driver.quit()
通過上面的兩個腳本,我們發現它優勢就是每一個腳本都是獨立的,任何一個腳本文件拿出來就能單獨運行;當然,缺點也很明顯,用例的開發與維護成本很高:
一個用例對應一個腳本,假如登陸發生變化,用戶名的屬性發生改變,不得不需要對每一個腳本進行修改,測試用例形成一種規模,我們可能將大量的工作用於腳本的維護,從而失去自動化的意義。
這種模式下數據和腳本是混在一起的,如果數據發生變也需要對腳本進行修改。這種模式下腳本的沒有可重復使用的概念
1.2 模塊化與類庫
我們會清晰的發現在上面的腳本中,其實有不少內容是重復的;於是我們就考慮能不能把重復的部分寫成一個公共的模塊,需要的時候進行調用,這樣就大大提高了我們編寫腳本的效率
login.py
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: login.py
@time: 2018-09-27 10:06
@desc:
'''
def login(driver):
driver.find_element_by_id('input1').clear()
driver.find_element_by_id('input1').send_keys('abcdd')
driver.find_element_by_id('input2').clear()
driver.find_element_by_id('input2').send_keys('abcdd')
driver.find_element_by_id('signin').click()
測試用例:
#!/usr/bin/env python # -*- codinfg:utf-8 -*- ''' @author: Jeff LEE @file: cnblog登錄.py @time: 2018-09-27 9:55 @desc: ''' import time from selenium import webdriver import login driver = webdriver.Firefox() #添加智能等待 driver.implicitly_wait(10) driver.get(r'https://passport.cnblogs.com/user/signin?ReturnUrl=https%3A%2F%2Fwww.cnblogs.com%2F') #登錄用戶 login.login(driver) #用例個性化操作 time.sleep(5) driver.quit()
通過閱讀上面的代碼發現,我們可以把腳本中相同的部分代碼獨立出來,形成模塊或庫;這樣做有兩方面的優點:
- 一方面提高了開發效率,不用重復的編寫相同的腳本;假如,我已經寫好一個登錄模塊,我后續需要做的就是在需要的地方調用,不同重復造輪子
- 另一方面方便了代碼的維護,假如登錄模塊發生了變化,我只用修改login.py 文件中登錄模塊的代碼即可,那么所有調用登錄模塊的腳本不用做任何修改
1.3 數據驅動
數據驅動應該是自動化的一個進步;從它的本意來講,數據的改變(更新)驅動自動化的執行,從而引起測試結果的改變。這顯然是一個非常高級的概念和想法。其實,我們可直白的理解成參數化,輸入數據的不同從而引起輸出結果的變化,下面介紹幾種讀取參數化的幾個例子:
1.3.1 讀取文件參數法
test1
abc,123 efg,456
file_reader.py
!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: file_reader.py
@time: 2018-09-27 17:00
@desc:
'''
import os
class fileReader(object):
def __init__(self, filepath):
if os.path.exists(filepath):
self.file = filepath
else:
raise FileNotFoundError('文件不存在!')
self._data = None
@property
def data(self):
# 如果是第一次調用data,讀取test文檔,否則直接返回之前保存的數據
if not self._data:
with open(self.file, 'rb') as f:
self._data = f.readlines()
return self._data
if __name__ =='__main__':
file_data = fileReader('test1').data
print(file_data)
執行結果:
['abc,123\n', 'efg,456']
對於像獲取那個參數,可以對列表進行操作。
1.3.2 yaml參數法
此方法作者比較喜歡
test.yaml
testinfo:
- id: test001
title: 進入通訊錄頁面
info: 打開APP
testcase:
- element_info: //*[@text="通訊錄" and @resource-id="com.camtalk.start:id/title"]
find_type: xpath
operate_type: click
info: 點擊通訊錄
check:
- element_info: //*[@text="全部" and @resource-id="com.camtalk.start:id/friend_rb_all"]
find_type: xpath
check: default_check
info: 打開通訊錄頁面成功
file_reader.py
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: file_reader.py
@time: 2018-09-27 17:00
@desc:
'''
import os
import yaml
class yamlReader(object):
def __init__(self, yamlf):
if os.path.exists(yamlf):
self.yamlf = yamlf
else:
raise FileNotFoundError('文件不存在!')
self._data = None
@property
def data(self):
# 如果是第一次調用data,讀取yaml文檔,否則直接返回之前保存的數據
if not self._data:
with open(self.yamlf, 'rb') as f:
self._data = list(yaml.safe_load_all(f)) # load后是個generator,用list組織成列表
return self._data
if __name__ == '__main__':
yaml_data=yamlReader('test.yaml').data
print(yaml_data)
運行結果:
[{'testcase': [{'info': '點擊通訊錄', 'find_type': 'xpath', 'operate_type': 'click', 'element_info': '//*[@text="通訊錄" and @resource-id="com.camtalk.start:id/title"]'}], 'testinfo': [{'info': '打開APP', 'title': '進入通訊錄頁面', 'id': 'test001'}], 'check': [{'info': '打開通訊錄頁面成功', 'find_type': 'xpath', 'check': 'default_check', 'element_info': '//*[@text="全部" and @resource-id="com.camtalk.start:id/friend_rb_all"]'}]}]
1.3.3 excel參數法
baidu.xlxs

file_reader.py
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: file_reader.py
@time: 2018-09-27 17:00
@desc:
'''
import os
from xlrd import open_workbook
class excelReader(object):
"""
讀取excel文件中的內容。返回list。
如:
excel中內容為:
| A | B | C |
| A1 | B1 | C1 |
| A2 | B2 | C2 |
如果 print(ExcelReader(excel, title_line=True).data),輸出結果:
[{A: A1, B: B1, C:C1}, {A:A2, B:B2, C:C2}]
如果 print(ExcelReader(excel, title_line=False).data),輸出結果:
[[A,B,C], [A1,B1,C1], [A2,B2,C2]]
可以指定sheet,通過index或者name:
ExcelReader(excel, sheet=2)
ExcelReader(excel, sheet='BaiDuTest')
"""
def __init__(self, excel, sheet=0, title_line=True):
if os.path.exists(excel):
self.excel = excel
else:
raise FileNotFoundError('文件不存在!')
self.sheet = sheet
self.title_line = title_line
self._data = list()
@property
def data(self):
if not self._data:
workbook = open_workbook(self.excel)
if type(self.sheet) not in [int, str]:
raise ('Please pass in <type int> or <type str>, not {0}'.format(type(self.sheet)))
elif type(self.sheet) == int:
s = workbook.sheet_by_index(self.sheet)
else:
s = workbook.sheet_by_name(self.sheet)
if self.title_line:
title = s.row_values(0) # 首行為title
for col in range(1, s.nrows):
# 依次遍歷其余行,與首行組成dict,拼到self._data中
self._data.append(dict(zip(title, s.row_values(col))))
else:
for col in range(0, s.nrows):
# 遍歷所有行,拼到self._data中
self._data.append(s.row_values(col))
return self._data
if __name__ == '__main__':
excel_data =excelReader('baidu.xlsx').data
print(excel_data)
運行結果:
[{'search': 'selenium 灰藍', 'name': 'lizf'}, {'search': 'Python selenium', 'name': 'abby'}]
1.3.4 csv參數法
test.csv

file_reader.py
#!/usr/bin/env python
# -*- codinfg:utf-8 -*-
'''
@author: Jeff LEE
@file: file_reader.py
@time: 2018-09-27 17:00
@desc:
'''
import os
import csv
class csvReader(object):
def __init__(self, filepath):
if os.path.exists(filepath):
self.file = filepath
else:
raise FileNotFoundError('文件不存在!')
self._data = None
@property
def data(self):
# 如果是第一次調用data,讀取csv文檔,否則直接返回之前保存的數據
if not self._data:
with open(self.file, 'rb') as f:
self._data = csv.reader(f)
return self._data
if __name__ == '__main__':
csv_data =excelReader('test.csv').data
print(csv_data)
運行結果(類似excel):
[{'number': 123.0, 'name': 'test1', 'email': 'test1@qq.com'}, {'number': 124.0, 'name': 'test2', 'email': 'test2@qq.com'}, {'number': 125.0, 'name': 'test3', 'email': 'test3@qq.com'}, {'number': 126.0, 'name': 'test4', 'email': 'test4@qq.com'}]
1.3.5 總結
不管我們讀取的是數組,還是字典、函數,又或者是csv、txt 、excel、yaml文件。我們實現了數據與腳本的分離,換句話說,我們實現了參數化。我們傳一千條數據,通過腳本的執行,可以返回一千條結果出來。
同樣的腳本執行不同的數據從而得到了不同的結果,是不是增強的腳本的復用性呢!?
其實,模塊化與參數化這對開發來說是完全沒有什么技術含量的;對於當初QTP 自動化工具來說地確是一個賣點,因為它面對的大多是不懂開發的測試,當然,隨着時代的發展,懂開發的測試人員越來越多
1.4 關鍵字驅動
理解了數據驅動,無非是把“數據”換成“關鍵字”,通過關鍵字的改變引起測試結果的改變。
關鍵字驅動用編程方式就不太容易表現了。QTP、robot framework 等都是以關鍵字驅動為主的自動化工具,因為這類工具主打的易用性,“填表格”式的關鍵字驅動幫我們封裝了很多底層的東西,我們只要考慮三個問題就可以了:我要做什么? 對誰做?怎么做?。
我們可以把selenium IDE 看做是一種關鍵字驅動的自動化工具,詳細可以去找網上其他資料
1.5 小結
這里簡單介紹了自動化測試的幾種不同的模型,雖然簡單闡述了他們的優缺點,但他們並非后者淘汰前者的關系,在實施自動化更多的是以需求為出發點,混合的來使用以上模型去解決問題;使我們的腳本更易於開發與維護。
