本文是我在學習了selenium+Python進行自動化測試後的總結,方便以後回顧和填坑吧(確實遇到了不少坑~~),也希望各位大神能多多指教!
本文主要包含Page Object模式設計測試用例,selenium元素定位和常用操作,同時包含自己在實踐中遇到的各種坑。
我設計的場景是用火狐瀏覽器登錄百度首頁,在首頁上測試輸入框,以及含有‘百度’的鏈接數量;然後點擊“設置”–>”高級搜索”,進入搜索設置頁面,檢查下拉菜單和單選框的屬性;然後在搜索框輸入搜索詞,在 搜索頁面得到搜索數目。
1. Page Object模式測試用例設計
PageObject是一種程序設計模式,將面向過程轉變爲面向對象(頁面對象),將測試對象及單個的測試步驟封裝在每個Page對象中,以page爲單位進行管理。可以使代碼複用,降低維護成本,提高程序可讀性和編寫效率。
PageObject可以將頁面定位和業務操作分開,分離測試對象(元素對象)和測試腳本(用例腳本),提高用例的可維護性。
我理解的就是將Page和test分開,這樣在一項改變時不會影響整體,變動減少。
Pages
首先看pages的設計,先有一個BasePage,這個BasePage包含所有待測page都能用到的公用方法。
驗證頁面的方法_validate_page()
使用了@abstractmethod
進行裝飾,基類不能被實例化,只有當子類繼承並且實現了改方法後子類纔可以實例化,這樣可以對不同的頁面進行不同的驗證。並定義了Search和Setting兩種方法,用於轉向不同頁面,使用@property
將方法轉爲屬性,方便實例化對象後使用。
對於首頁的設計,如下:
在首頁中,完成了對頁面驗證_validate_page()
的重寫,並對含有’百度’的鏈接數和‘新聞’鏈接狀態進行了返回。
對於搜索設置頁面,如下:
在SettingRegion()類實現點擊‘設置’進入設置頁面,在SettingPage類裏完成對該頁面一些元素的檢查,包括搜索時間範圍的下來菜單,檢查其包含的下來選項數量,選擇其中一項,查找範圍的單選按鈕,選中一項。
對於搜索結果頁面,如下:
主要完成輸入搜索詞點擊搜索,SearchRegion()類,然後在搜索結果頁面定位到結果語句,並將語句中的數值返回(即搜索到的條目數)。
Test
我使用的是unittest來組織測試,pip install unittest
安裝後,先在test的基類中完成測試前/後的方法,即setUp()和tearDown(),這會在每次test方法前後調用,若是想應用於整個測試類,可以使用類方法setUpClass()和tearDownClass(),並使用@classmethod
修飾。基類如下:
import unittest
from selenium import webdriver
class BaseTestCase(unittest.TestCase):
"""docstring for BaseTestCase"""
def setUp(self):
# create a new Firefox session
profile_dir = r'C:\Users\czhou012\AppData\Roaming\Mozilla\Firefox\Profiles\iewanxoz.default'
profile = webdriver.FirefoxProfile(profile_dir)
self.driver = webdriver.Firefox(profile)
self.driver.implicitly_wait(30)
self.driver.maximize_window()
# navigate to the application home page
self.driver.get('https://www.baidu.com/')
def tearDown(self):
# close the browser window
self.driver.quit()
最後寫測試類,繼承於BaseTestCase():
import unittest
from pages.homepage import HomePage
from pages.settingpage import SettingRegion
from base.basetestcase import BaseTestCase
from base.data import GetData
from ddt import ddt, data, unpack
@ddt
class PageTest(BaseTestCase):
"""docstring for PageTest"""
#@unittest.skip('not run')
def test_home_page(self):
hp = HomePage(self.driver)
self.assertEqual(4,hp.links_count)
self.assertTrue(hp.news_able)
#@unittest.skip('not run')
def test_set_page(self):
set_page = HomePage(self.driver).Setting.set_to()
self.assertEqual(5,set_page.num_show_time)
self.assertEqual('最近一天',set_page.select_one_day)
self.assertTrue(set_page.radio_select)
#@unittest.skip('no run ')
@data(*GetData().data)
@unpack
def test_search_result(self,term,expect_num):
sp = HomePage(self.driver).Search.searchFor(term)
self.assertGreater(sp.result_num,expect_num)
if __name__ == '__main__':
unittest.main(verbosity=2)
分別完成首頁的測試,設置頁面的測試,和搜索結果頁面的測試。
數據驅動
在搜索結果的測試中我使用了數據驅動,即將數據參數化,適用於大量數據重複操作中,在這裏我新建了一個csv數據文檔,隨意編寫了搜索詞條和必須大於的返回結果數目。
對應編寫用於讀取數據的模塊:
#coding:utf-8
import csv
class GetData(object):
"""docstring for GetDat"""
def __init__(self, file_name=None):
if file_name != None:
self.file_name = file_name
else:
self.file_name = r'D:\TEST\selenium\my\testdata.csv'
# create an empty list to store rows
@property
def data(self):
rows = []
# open the CSV file
with open(self.file_name,'r',encoding='utf-8') as data_file:
# create a CSV Reader from CSV file
reader = csv.reader(data_file)
# skip the headers
next(reader, None)
# add rows from reader to list
for row in reader:
rows.append(row)
num_list = list(map(int,list(zip(*rows))[1]))
term_list = list(zip(*rows))[0]
return list(zip(term_list,num_list))
這樣調用該對象的data即可返回數據[('phones', 3), ('music', 5), ('iphone 5s', 10)]
。
使用ddt模塊pip install ddt
, 在測試類前加修飾@ddt
,在使用數據驅動的測試方法前使用@data(數據集)
,獲取數據,使用@unpack
會自動將數據對應到多個參數上。單獨運行可以看出結果:
去掉註釋整體運行可得結果:
填坑
坑一:直接使用腳本啓動Firefox瀏覽器,會發現沒有瀏覽器插件,打開相同的網址,網頁展示也不一樣,這會給定位元素造成很大困擾。原因:用腳本去打開瀏覽器時候,其實是重新打開了一個進程,跟手動打開瀏覽器不是一個進程。解決方法:在啓動時webdriver.Firefox(profile)傳入參數profile,具體可參考此博文
坑二:對於需要懸停操作顯示元素的定位:
百度首頁的搜索設置選項需要在‘設置’懸停顯示,需要加入鼠標移動到指定元素中央的操作。
坑三:選擇下拉菜單選項時可能會有報錯:‘Element could not be scrolled into view’,加入等待時間即可。
坑四:逐行讀取csv文件後獲得的二維列表中,Number列是str類型,這樣不能直接比較,我這裏採用的方法是將該列取出轉爲int,再和前一列合併。
坑五:我定位到的是百度搜索結果的一句話。需要在這句話中找出用逗號分隔表示的數值,這裏沒用正則表達式,而是使用isdigit()
來判斷,需要注意返回的是類似於[‘1’,’2’,’3’,’4’]這樣的列表,還需要拼接後再轉爲int類型。
新手學習,歡迎指教!