首先你得知道什么是Selenium键痛?
Selenium是一個(gè)基于瀏覽器的自動(dòng)化測(cè)試工具炫彩,它提供了一種跨平臺(tái)、跨瀏覽器的端到端的web自動(dòng)化解決方案絮短。Selenium主要包括三部分:Selenium IDE江兢、Selenium WebDriver 和Selenium Grid。
Selenium IDE:Firefox的一個(gè)擴(kuò)展丁频,它可以進(jìn)行錄制回放杉允,并把錄制的操作以多種語(yǔ)言(例如java、python等)的形式導(dǎo)出成測(cè)試用例席里。
Selenium WebDriver:提供Web自動(dòng)化所需的API叔磷,主要用作瀏覽器控制、頁(yè)面元素選擇和調(diào)試奖磁。不同的瀏覽器需要不同的WebDriver改基。
Selenium Grid:提供了在不同機(jī)器的不同瀏覽器上運(yùn)行selenium測(cè)試的能力。
下面我會(huì)使用思維導(dǎo)圖目錄結(jié)構(gòu)介紹基礎(chǔ)測(cè)試框架咖为,編寫測(cè)試用例進(jìn)行功能測(cè)試用例秕狰,希望對(duì)您的學(xué)習(xí)有所幫助嵌洼。
設(shè)計(jì)思路
框架采用python3 + selenium3 + PO + yaml + ddt + unittest等技術(shù)編寫成基礎(chǔ)測(cè)試框架,能適應(yīng)日常測(cè)試工作需要封恰。
使用Page Object模式將頁(yè)面定位和業(yè)務(wù)操作分開(kāi)麻养,分離測(cè)試對(duì)象(元素對(duì)象)和測(cè)試腳本(用例腳本),一個(gè)頁(yè)面建一個(gè)對(duì)象類诺舔,提高用例的可維護(hù)性鳖昌;
使用yaml管理頁(yè)面控件元素?cái)?shù)據(jù)和測(cè)試用例數(shù)據(jù)。例如元素ID等發(fā)生變化時(shí)低飒,不需要去修改測(cè)試代碼许昨,只需要在對(duì)應(yīng)的頁(yè)面元素yaml文件中修改即可;
分模塊管理褥赊,互不影響糕档,隨時(shí)組裝,即拿即用拌喉。
測(cè)試框架分層設(shè)計(jì)
把常見(jiàn)的操作和查找封裝成基礎(chǔ)類速那,不管是什么產(chǎn)品,可直接拿來(lái)復(fù)用
業(yè)務(wù)層主要是封裝對(duì)象頁(yè)面類尿背,一個(gè)頁(yè)面建一個(gè)類端仰,業(yè)務(wù)層頁(yè)面繼承基礎(chǔ)層
用例層針對(duì)產(chǎn)品頁(yè)面功能進(jìn)行構(gòu)造模擬執(zhí)行測(cè)試
框架層提供基礎(chǔ)組件,支撐整個(gè)流程執(zhí)行及功能擴(kuò)展田藐,給用例層提供各頁(yè)面的元素?cái)?shù)據(jù)荔烧、用例測(cè)試數(shù)據(jù),測(cè)試報(bào)告輸出等
測(cè)試框架目錄結(jié)構(gòu)
如下思維導(dǎo)圖目錄結(jié)構(gòu)介紹:
編寫用例方法
如果對(duì)軟件測(cè)試汽久、接口測(cè)試鹤竭、自動(dòng)化測(cè)試、面試經(jīng)驗(yàn)交流景醇。感興趣可以關(guān)注我們愛(ài)碼小士臀稚,公眾號(hào)內(nèi)會(huì)有不定期的發(fā)放免費(fèi)的資料鏈接,這些資料都是從各個(gè)技術(shù)網(wǎng)站搜集啡直、整理出來(lái)的烁涌,如果你有好的學(xué)習(xí)資料可以私聊發(fā)我,我會(huì)注明出處之后分享給大家酒觅。
testinfo:
- id: test_login001
title: 登錄測(cè)試
info: 打開(kāi)抽屜首頁(yè)
testcase:
- element_info: login-link-a
find_type: ID
operate_type: click
info: 打開(kāi)登錄對(duì)話框
- element_info: mobile
find_type: ID
operate_type: send_keys
info: 輸入手機(jī)號(hào)
- element_info: mbpwd
find_type: ID
operate_type: send_keys
info: 輸入密碼
- element_info: //input[@class='keeplogin']
find_type: XPATH
operate_type: click
info: 單擊取消自動(dòng)登錄單選框
- element_info: //span[text()='登錄']
find_type: XPATH
operate_type: click
info: 單擊登錄按鈕
- element_info: userProNick
find_type: ID
operate_type: perform
info: 鼠標(biāo)懸停賬戶菜單
- element_info: //a[@class='logout']
find_type: XPATH
operate_type: click
info: 選擇退出
check:
- element_info: //div[@class='box-mobilelogin']
/div[1]/span
find_type: XPATH
info: 檢查輸入手機(jī)號(hào)或密碼撮执,登錄異常提示
- element_info: userProNick
find_type: ID
info: 成功登錄
- element_info: reg-link-a
find_type: ID
info: 檢查退出登錄是否成功
login.yaml
例如,我們要新增登錄功能測(cè)試用例:
首先舷丹,只需在testyaml目錄下新增一個(gè)頁(yè)面對(duì)象yaml文件抒钱,參考login.yaml格式編寫即可。這些文件是提供給封裝頁(yè)面對(duì)象類調(diào)用并執(zhí)行定位識(shí)別操作。
-
id: test_login001.1
detail : 手機(jī)號(hào)和密碼為空登錄
screenshot : phone_pawd_empty
data:
phone: ""
password: ""
check :
- 手機(jī)號(hào)不能為空
-
id: test_login001.2
detail : 手機(jī)號(hào)為空登錄
screenshot : phone_empty
data :
phone: ""
password : aa
check :
- 手機(jī)號(hào)不能為空
-
id: test_login001.3
detail : 密碼為空登錄
screenshot : pawd_empty
data :
phone : 13511112222
password: ""
check :
- 密碼不能為空
-
id: test_login001.4
detail : 非法手機(jī)號(hào)登錄
screenshot : phone_error
data :
phone : abc
password: aa
check :
- 手機(jī)號(hào)格式不對(duì)
-
id: test_login001.5
detail : 手機(jī)號(hào)或密碼不匹配
screenshot : pawd_error
data :
phone : 13511112222
password: aa
check :
- 賬號(hào)密碼錯(cuò)誤
-
id: test_login001.6
detail : 手機(jī)號(hào)和密碼正確
screenshot : phone_pawd_success
data :
phone : 13865439800
password: ********
check :
- yingoja
login_data.yaml
login_data.yaml
其次谋币,在testdata目錄下新增一個(gè)login_data.yaml文件提供給登錄接口傳參的測(cè)試數(shù)據(jù)仗扬,編寫格式參考login_data.yaml文件。
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'YinJia'
import os,sys
sys.path.append(os.path.dirname(os.path.dirname
(os.path.dirname(__file__))))
from config import setting
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains
import ActionChains
from selenium.webdriver.common.by import By
from public.page_obj.base import Page
from time import sleep
from public.models.GetYaml import getyaml
testData = getyaml(setting.TEST_Element_YAML
+ '/' + 'login.yaml')
class login(Page):
"""
用戶登錄頁(yè)面
"""
url = '/'
dig_login_button_loc = (By.ID, testData.
get_elementinfo(0)) def dig_login(self):
"""
首頁(yè)登錄
:return:
"""
self.find_element(*self.dig_login_button_loc)
.click() sleep(1)
# 定位器蕾额,通過(guò)元素屬性定位元素對(duì)象
# 手機(jī)號(hào)輸入框
login_phone_loc = (By.ID,testData.
get_elementinfo(1)) # 密碼輸入框
login_password_loc = (By.ID,testData.
get_elementinfo(2)) # 取消自動(dòng)登錄
keeplogin_button_loc = (By.XPATH,testData.
get_elementinfo(3)) # 單擊登錄
login_user_loc = (By.XPATH,testData.
get_elementinfo(4)) # 退出登錄
login_exit_loc = (By.ID, testData.
get_elementinfo(5)) # 選擇退出
login_exit_button_loc = (By.XPATH,testData.
get_elementinfo(6))def login_phone(self,phone):
"""
登錄手機(jī)號(hào)
:param username:
:return:
"""
self.find_element(*self.login_phone_loc).
send_keys(phone)def login_password(self,password):
"""
登錄密碼
:param password:
:return:
"""
self.find_element(*self.login_password_loc).
send_keys(password) def keeplogin(self):
"""
取消單選自動(dòng)登錄
:return:
"""
self.find_element(*self.keeplogin_button_loc).
click()def login_button(self):
"""
登錄按鈕
:return:
"""
self.find_element(*self.login_user_loc).click()
def login_exit(self):
"""
退出系統(tǒng)
:return:
"""
above = self.find_element(*self.login_exit_loc)
ActionChains(self.driver).move_to_element(above).
perform() sleep(2)
self.find_element(*self.login_exit_button_loc)
.click()def user_login(self,phone,password):
"""
登錄入口
:param username: 用戶名
:param password: 密碼
:return:
"""
self.open()
self.dig_login()
self.login_phone(phone)
self.login_password(password)
sleep(1)
self.keeplogin()
sleep(1)
self.login_button()
sleep(1)
phone_pawd_error_hint_loc = (By.XPATH,testData.
get_CheckElementinfo(0))
user_login_success_loc = (By.ID,testData.
get_CheckElementinfo(1))
exit_login_success_loc = (By.ID,testData.
get_CheckElementinfo(2))
# 手機(jī)號(hào)或密碼錯(cuò)誤提示
def phone_pawd_error_hint(self):
return self.find_element(*self.phone_pawd_error_
hint_loc).text# 登錄成功用戶名
def user_login_success_hint(self):
return self.find_element(*self.user_login_
success_loc).text # 退出登錄
def exit_login_success_hint(self):
return self.find_element(*self.exit_login_
success_loc).textloginPage.py
然后早芭,在page_obj目錄下新增一個(gè)loginPage.py文件,是用來(lái)封裝登錄頁(yè)面對(duì)象類诅蝶,執(zhí)行登錄測(cè)試流程操作退个。
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'YinJia'
import os,sys
sys.path.append(os.path.dirname(os.path.
dirname(__file__)))
import unittest,ddt,yaml
from config import setting
from public.models import myunit,screenshot
from public.page_obj.loginPage import login
from public.models.log import Log
try:
f =open(setting.TEST_DATA_YAML + '/' + 'login_data.yaml',encoding='utf-8')
testData = yaml.load(f)
except FileNotFoundError as file:
log = Log()
log.error("文件不存在:{0}".format(file))
@ddt.ddt
class Demo_UI(myunit.MyTest):
"""抽屜新熱榜登錄測(cè)試"""
def user_login_verify(self,phone,password):
"""
用戶登錄
:param phone: 手機(jī)號(hào)
:param password: 密碼
:return:
"""
login(self.driver).user_login(phone,password)
def exit_login_check(self):
"""
退出登錄
:return:
"""
login(self.driver).login_exit()
@ddt.data(*testData)
def test_login(self,datayaml):
"""
登錄測(cè)試
:param datayaml: 加載login_data登錄測(cè)試數(shù)據(jù)
:return:
"""
log = Log()
log.info("當(dāng)前執(zhí)行測(cè)試用例ID-> {0} ; 測(cè)試點(diǎn)-> {1}".format(datayaml['id'],datayaml['detail']))
# 調(diào)用登錄方法
self.user_login_verify(datayaml['data']['phone'],
datayaml['data']['password'])
po = login(self.driver)
if datayaml['screenshot'] == 'phone_pawd_success':
log.info("檢查點(diǎn)-> {0}".format
(po.user_login_success_hint()))
self.assertEqual(po.user_login_success_hint(), datayaml['check'][0], "成功登錄,返回實(shí)際結(jié)果是->: {0}".format(po.user_login_success_hint()))
log.info("成功登錄调炬,返回實(shí)際結(jié)果是->: {0}".format(po.user_login_success_hint()))
screenshot.insert_img(self.driver, datayaml
['screenshot'] + '.jpg')
log.info("-----> 開(kāi)始執(zhí)行退出流程操作")
self.exit_login_check()
po_exit = login(self.driver)
log.info("檢查點(diǎn)-> 找到{0}元素,表示退出成功语盈!".format(po_exit.exit_login_success_hint()))
self.assertEqual(po_exit.exit_login_success_hint(),
'注冊(cè)',"退出登錄,返回實(shí)際結(jié)果是->: {0}".format(po_exit.exit_login_success_hint()))
log.info("退出登錄缰泡,返回實(shí)際結(jié)果是->: {0}".format(po_exit.exit_login_success_hint()))
else:
log.info("檢查點(diǎn)-> {0}".format(po.phone
_pawd_error_hint()))
self.assertEqual(po.phone_pawd_error_hint(),
datayaml['check'][0] , "異常登錄刀荒,返回實(shí)際結(jié)果是->: {0}".format(po.phone_pawd_error_hint()))
log.info("異常登錄,返回實(shí)際結(jié)果是->: {0}".format(po.phone_pawd_error_hint()))
screenshot.insert_img(self.driver,datayaml
['screenshot'] + '.jpg')
if __name__=='__main__':
unittest.main()
login_sta.py
最后棘钞,在testcase目錄下創(chuàng)建測(cè)試用例文件login_sta.py缠借,采用ddt數(shù)據(jù)驅(qū)動(dòng)讀取yaml測(cè)試數(shù)據(jù)文件
綜上所述,編寫用例方法只需要按以上四個(gè)步驟創(chuàng)建->編寫即可武翎。
執(zhí)行如下主程序烈炭,可看輸出的實(shí)際結(jié)果溶锭。
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'YinJia' import os,sys
sys.path.append(os.path.dirname(__file__))
from config import setting
import unittest,time
from package.HTMLTestRunner import HTMLTestRunner
from public.models.newReport import new_report
from public.models.sendmail import send_mail
# 測(cè)試報(bào)告存放文件夾宝恶,如不存在,則自動(dòng)創(chuàng)建
一個(gè)report目錄 if not os.path.exists(setting.TEST_REPORT):os.makedirs
(setting.TEST_REPORT + '/' + "screenshot")
def add_case(test_path=setting.TEST_DIR):
"""加載所有的測(cè)試用例"""
discover = unittest.defaultTestLoader.discover
(test_path, pattern='*_sta.py')
return discover
def run_case(all_case,result_path=setting.TEST_REPORT):
"""執(zhí)行所有的測(cè)試用例"""
now = time.strftime("%Y-%m-%d %H_%M_%S")
filename = result_path + '/' + now + 'result.html'
fp = open(filename,'wb')
runner = HTMLTestRunner(stream=fp,title='
抽屜新熱榜UI自動(dòng)化測(cè)試報(bào)告',
description='環(huán)境:windows 7 瀏覽器:chrome',
tester='Jason')
runner.run(all_case)
fp.close()
report = new_report(setting.TEST_REPORT)
#調(diào)用模塊生成最新的報(bào)告
send_mail(report) #調(diào)用發(fā)送郵件模塊
if __name__ =="__main__":
cases = add_case()
run_case(cases)
測(cè)試結(jié)果展示
HTML報(bào)告日志
HTML報(bào)告點(diǎn)擊截圖趴捅,彈出截圖
測(cè)試報(bào)告通過(guò)的日志
自動(dòng)截圖存放指定的目錄
郵件測(cè)試報(bào)告