最近接手商城的項目,針對後臺測試,功能比較簡單,可是流程比較繁多,涉及到先後臺的交叉測試。在對整個項目進行第一輪測試完成以後,考慮之後迴歸測試任務比較重,爲了減輕迴歸測試的工做量,因此考慮後臺能夠進行部分自動化測試。css
以前一個項目使用robotframework進行迴歸測試,瞭解了python語言,因此此次就使用python+selenium進行自動化迴歸測試了。html
運行工具pycharm2017python
登陸操做步驟說明web
根據上面的步驟提示下面代碼顯示登陸操做,以下瀏覽器
#coding=utf-8 from selenium import webdriver import unittest import sys reload(sys) sys.setdefaultencoding('utf8') class TestLogin(unittest.TestCase): # 指定瀏覽器 def setUp(self): self.driver = webdriver.Firefox() # 打開url self.driver.get("http://192.168.10.6/login") # 登陸操做 def test_login(self): title = self.driver.title print title now_url = self.driver.current_url print now_url username = "test001" password = "testgood001" # 執行登陸操做 #用戶名的定位 self.driver.find_element_by_id("username").clear() self.driver.find_element_by_id("username").send_keys(username) #密碼的定位 self.driver.find_element_by_id("password").clear() self.driver.find_element_by_id("password").send_keys(password) # 點擊登陸 self.driver.find_element_by_css_selector(".btn.btn-success.btn-block").click() # 登陸成功斷言 login_name = self.driver.find_element_by_xpath('html/body/div[3]/div[2]/ul/li[1]/a/strong').text login_name = login_name.strip('您好:') assert login_name == username # 關閉瀏覽器 def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
上面代碼顯示的是登陸成功的正經常使用例;實際操做中,針對登陸不單單有正經常使用例,還有異經常使用例,如用戶名爲空,密碼爲空,用戶名錯誤,密碼錯誤等;咱們不能一個用例編寫一個py文件,若是這樣操做從本質而言相反增長了工做量。app
既然問題出來了,那麼如何解決這個問題呢?框架
思路:針對登陸而言,全部的步驟都是同樣的,惟一不一樣的就是登陸的用戶名和密碼,因此咱們能夠封裝登陸步驟,而後只須要專一不一樣測試用例中的登陸的用戶名和密碼的驗證便可。dom
這裏爲了後續測試的簡便,使用了selenium中的po模式,即針對每一個功能的操做頁面進行封裝,然後在針對該頁面進行測試用例的編寫。如這裏的登陸頁面,咱們須要針對登陸頁面進行封裝操做,把登陸頁面中的用戶名、密碼和登陸按鈕的定位進行封裝,這樣用例中只關注輸入不一樣的用戶名和密碼進行驗證便可。python2.7
說明:函數
這裏瀏覽器的打開和關閉也進行了封裝,放在myunit.py中。
總體設計的結構如圖所示
models/driver.py中封裝了打開瀏覽器的操做,這裏使用的火狐瀏覽器進行操做。代碼以下
# -*-coding:utf-8-*- # _author_ = "janehost" from selenium.webdriver import Remote from selenium import webdriver import sys # 啓動瀏覽器 reload(sys) sys.setdefaultencoding('utf8') def browser(): driver = webdriver.Firefox() return driver if __name__ == '__main__': dr = browser() dr.get("http://192.168.10.6/login") dr.quit()
models/myunit.py中主要封裝了瀏覽器的啓動和關閉的操做,代碼以下
# -*-coding:utf-8-*- # _author_ = "janehost" import unittest,sys from selenium import webdriver from driver import browser reload(sys) sys.setdefaultencoding('utf8') class MyTest(unittest.TestCase): def setUp(self): self.driver = browser() self.driver.implicitly_wait(10) self.driver.maximize_window() def tearDown(self): self.driver.quit() if __name__ == '__main__': unittest.main()
models/function.py中主要封裝了截圖的操做方法,代碼參考以下
# -*-coding:utf-8-*- # _author_ = "janehost" from selenium import webdriver import os,sys reload(sys) sys.setdefaultencoding('utf8') # 截圖函數 def insert_img(driver, file_name): base_dir = os.path.dirname(os.path.dirname(__file__)) base_dir = str(base_dir) base_dir = base_dir.replace('\\', '/') base = base_dir.split('test_case')[0] file_path = base + "report/image/" + file_name driver.get_screenshot_as_file(file_path) if __name__ == '__main__': driver = webdriver.Firefox() driver.get("http://192.168.10.6/login") insert_img(driver, 'login.jpg') driver.quit()
下面就是po模式的重點,針對頁面的封裝,首先建立一個page頁面的基本頁面,page_obj\base.py代碼以下
# -*-coding:utf-8-*- # _author_ = "janehost" from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import sys reload(sys) sys.setdefaultencoding('utf8') class Page(object): ''' 頁面基礎類,用於全部頁面的繼承 ''' base_url = "http://192.168.10.6/login" def __init__(self, selenium_driver, base_url=base_url, parent=None): self.base_url = base_url self.driver = selenium_driver self.timeout = 30 self.parent = parent def _open(self, url): url = self.base_url + url self.driver.get(url) assert self.on_page(), 'Did not land on %s' % url def open(self): self._open(self.url) def on_page(self): #return (self.driver.current_url).encode('utf-8') == (self.base_url + self.url) return self.driver.current_url.encode('utf-8') == (self.base_url + self.url) def find_element(self, *loc): # return self.driver.find_element(*loc) try: # 確保全部元素是可見的 # 注意:如下入參爲元組的元素,須要加*。python存在這種特性,就是將入參放在元組裏。 #WebDriverWait(self.driver,10).until(lambda driver: driver.find_element(*loc).is_displayed()) # 注意:如下入參自己是元組,不須要加* WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(loc)) return self.driver.find_element(*loc) except: print u"%s 頁面中未能找到 %s 元素"%(self, loc) def find_elements(self, *loc): return self.driver.find_elements(*loc) def script(self, src): return self.driver.execute_script(src) def swtich_frame(self, loc): return self.driver.swith_to_frame(loc) def send_keys(self, loc, value, clear_first=True, click_first=True): try: # getattr至關於self.loc loc = getattr(self, "_%s" % loc) if click_first: self.find_element(*loc).click() if clear_first: self.find_element(*loc).clear() self.find_element(*loc).send_keys(value) except ArithmeticError: print u"%s 頁面中未能找到 %s 元素" % (self, loc)
登陸頁面元素的封裝page_obj\loginPage.py,代碼以下
# -*-coding:utf-8-*- # _author_ = "janehost" """ 思路:建立登陸頁面對象,對用戶登陸頁面上的用戶名/密碼輸入框、登陸按鈕和 提示信息等元素的定位進行封裝。 """ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from base import Page from time import sleep class login(Page): ''' 用戶登陸界面 ''' url = '/' # 登陸用戶名的定位 login_username_loc = (By.ID, 'username') # 登陸密碼的定位 login_password_loc = (By.ID,'password') # 登陸按鈕的定位 login_button_loc = (By.CSS_SELECTOR,'.btn.btn-success.btn-block') # 登陸錯誤提示的定位 login_error_loc = (By.ID,'error_msg') # 登陸成功用戶名信息 login_user_success_loc = (By.XPATH, 'html/body/div[3]/div[2]/ul/li[1]/a/strong') # 登陸用戶名 def login_username(self, username): self.find_element(*self.login_username_loc).clear() self.find_element(*self.login_username_loc).send_keys(username) # 登陸密碼 def login_password(self, password): self.find_element(*self.login_password_loc).clear() self.find_element(*self.login_password_loc).send_keys(password) # 登陸按鈕 def login_button(self): self.find_element(*self.login_button_loc).click() # 統一登陸入口 def user_login(self, username="testuser01", password="testgood001"): # 獲取用戶名和頁面登陸 self.open() self.login_username(username) self.login_password(password) self.login_button() sleep(3) # 登陸錯誤提示信息 def login_error_hint(self): return self.find_element(*self.login_error_loc).text # 登陸成功用戶名信息 def login_user_success(self): #return self.find_element(*self.login_user_success_loc).text username = self.find_element(*self.login_user_success_loc).text username = username.strip('您好:') return username
登陸測試用例信息test_case\login_sta.py,代碼以下
# -*-coding:utf-8-*- # _author_ = "janehost" from time import sleep import unittest, random, sys from models import myunit, function from page_obj.loginPage import login sys.path.append("./models") sys.path.append("./page_obj") reload(sys) sys.setdefaultencoding('utf8') class loginTest(myunit.MyTest): ''' 測試用戶登陸 ''' def user_login_verify(self, username="", password=""): login(self.driver).user_login(username, password) def test_login1(self): '''用戶名、密碼爲空登陸''' self.user_login_verify() po = login(self.driver) self.assertEqual(po.login_error_hint(), '用戶名或密碼不能爲空') function.insert_img(self.driver, "user_pawd_empty.jpg") def test_login2(self): '''用戶名正確,密碼爲空登陸驗證''' self.user_login_verify(username="ces") po = login(self.driver) self.assertEqual(po.login_error_hint(), "用戶名或密碼不能爲空") function.insert_img(self.driver,"pawd_empty.jpg") def test_login3(self): '''用戶名爲空,密碼正確''' self.user_login_verify(password="12334ddf") po = login(self.driver) self.assertEqual(po.login_error_hint(),"用戶名或密碼不能爲空") function.insert_img(self.driver, "user_empty.jpg") def test_login4(self): '''用戶名和密碼不匹配''' character = random.choice('abcdefghijklmnopqrstuvwxyz') username = "sdw" + character self.user_login_verify(username=username, password="2sdfd") po = login(self.driver) self.assertEqual(po.login_error_hint(), "用戶名或密碼錯誤") function.insert_img(self.driver, "user_pass_error.jpg") def test_login5(self): '''用戶名、密碼正確''' self.user_login_verify(username="adtest" , password="4dscsdx") sleep(3) po = login(self.driver) self.assertEqual(po.login_user_success(), u'adtest') function.insert_img(self.driver, "user_pwd_true.jpg") if __name__ == '__main__': unittest.main()
這樣登陸的測試用例就完成了。使用po模式以後,若是頁面ui發生變化,咱們只須要修改元素的定位方法,而不須要改動整個框架,相對而言比較快捷。
參考:
selenium2 python自動化測試實戰(蟲師)