最近接手商城的項目,針對后台測試,功能比較簡單,但是流程比較繁多,涉及到前后台的交叉測試。在對整個項目進行第一輪測試完成之后,考慮以后回歸測試任務比較重,為了減輕回歸測試的工作量,所以考慮后台可以進行部分自動化測試。
之前一個項目使用robotframework進行回歸測試,了解了python語言,所以這次就使用python+selenium進行自動化回歸測試了。
配置環境
- python2.7
- 運行工具pycharm2017
流程說明
登錄操作步驟說明
- 打開登錄url,如http://192.168.10.6/login
- 在用戶名輸入框中輸入登錄的用戶名,如test001
- 在密碼是輸入框中輸入密碼,如testgood001
- 點擊登錄頁面的登錄按鈕,
- 登錄成功頁面,斷言登錄成功
線性操作
根據上面的步驟提示下面代碼顯示登錄操作,如下
#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文件,如果這樣操作從本質而言相反增加了工作量。
既然問題出來了,那么如何解決這個問題呢?
思路:針對登錄而言,所有的步驟都是一樣的,唯一不同的就是登錄的用戶名和密碼,所以我們可以封裝登錄步驟,然后只需要專注不同測試用例中的登錄的用戶名和密碼的驗證即可。
這里為了后續測試的簡便,使用了selenium中的po模式,即針對每個功能的操作頁面進行封裝,而后在針對該頁面進行測試用例的編寫。如這里的登錄頁面,我們需要針對登錄頁面進行封裝操作,把登錄頁面中的用戶名、密碼和登錄按鈕的定位進行封裝,這樣用例中只關注輸入不同的用戶名和密碼進行驗證即可。
說明:
這里瀏覽器的打開和關閉也進行了封裝,放在myunit.py中。
po模式設計
整體設計的結構如圖所示
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自動化測試實戰(蟲師)