Selenium2+Python自動化測試實戰


     本人在網上查找了很多做自動化的教程和實例,偶然的一個機會接觸到了selenium,覺得非常好用。后來就在網上查閱各種selenium的教程,但是網上的東西真的是太多了,以至於很多東西參考完后無法系統的學習和應用,有一次在網上隨意搜索,找到了-蟲師-寫的《Selenium2自動化測試實戰基於Python語言》,覺得真心不錯,內容也很調理,為了方便自己學習和知識的整理,就把其中蟲師編寫的自動化項目教程整理一下,有興趣的可以去參看蟲師的博客http://www.cnblogs.com/fnng/

以下整理的只是書中自動化項目的知識內容,介紹怎么搭建自動化測試框架、執行自動化測試用例、生成自動化測試報告、發送測試報告郵件....,具體的Selenium和python語言基礎不做介紹

一、項目結構介紹

 下面逐級介紹此目錄與文件的作用

mztstpro/

|-----bbs/

|       |-----data/

|       |-----report/

|                |------image/

|       |-----test_case/

|                |------models/

|                         |----driver.py

|                         |----function.py

|                         |----myunit.py

|                |------page_obj/

|                         |----*Page.py

|                |------*_sta.py

|-----driver/

|-----package/

|-----run_bbs_test.py

|-----startip.bat

|-----自動化測試項目說明文檔.docx

1.mztestpro測試項目

bbs:用於存放BBS項目的測試用例、測試報告和測試數據等。

driver:用於存放瀏覽器驅動。如selenium-server-standalone-2.47.0jar、chromedriver.exe、IEDriverServer.exe等。在執行測試前根據執行場景將瀏覽器驅動復制到系統環境path目錄下。

package:用於存放自動化所用到的擴展包。例如:HTMLTestRunner.py屬於一個單獨模塊

run_bbs_test.py:項目主程序。用來運行社區(BBS)自動化用例。

startup.bat:用於啟動selenium server,默認啟動driver目錄下的selenium-server-standalone-2.44.0.jar。

自動化測試項目說明文檔.docx:介紹當前項目的架構、配置和使用說明。

2.bbs目錄

data:該目錄用來存放測試相關數據。

report:用於存放HTML測試報告。其下面創建了image目錄用於存放測試過程中的截圖。

test_case:測試用例目錄,用於存放測試用例及相關模塊。

3.test_case

models:該目錄下存放了一些公共的配置函數及公共類。

page_obj:該目錄用於存放測試用例的頁面對象(Page Object)。根據自定義規則,以“*Page.py”命名的文件為封裝的頁面對象文件。

*_sta.py:測試用例文件。根據測試文件匹配規則,以“*_sta.py”命名的文件被當作自動化測試用例執行。

二、編寫公共模塊

  首先定義驅動文件:

  ...\mztestpro\bbs\test_case\models\driver.py

driver.py

# __author__ = 'Ztiny'
# -*-coding:utf-8-*-

from selenium.webdriver import Remote
from selenium import webdriver

# 啟動瀏覽器驅動
def browser():
    driver = webdriver.Firefox()
    # host = '192.168.0.132:5555' #運行主機 :端口號(默認本機:127.0.0.1:4444)
    # dc = {'browserName':'internet explorer','version':'','platfrom':'WINDOWS','javascriptEnabled':True}
    # # dc = {'browserName':'firefox','version':'','platfrom':'ANY','javascriptEnabled':True,'marionette':False,}#指定瀏覽器 ('chrome','firefox')
    # driver = Remote(command_executor='http://' + host + '/wd/hub',
    #                 desired_capabilities=dc)
    return driver

if __name__ == '__main__':
    dr = browser()
    dr.get("http://www.mayi.com")
    dr.quit()

 

  定義瀏覽器驅動函數browser(),該函數可以進行配置,根據我們的需要,配置測試用例在不同的主機及瀏覽器下運行。

  自定義測試框架類:

  ...\mztestpro\bbs\test_case\models\myunit.py

 myunit.py

# __author__ = 'Ztiny'
#-*-coding:utf-8-*-
from selenium import webdriver
from driver import browser
import unittest

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()

 

  定義MyTest()類用於集成unittest.TestCase類,因為筆者創建的所有測試類中setUp()與tearDown()方法所做的事情相同,所以,將他們抽象為MyTest()類,好處就是在編寫測試用例時不再考慮這兩個方法的實現。

  定義截圖函數:

  ...\mztestpro\bbs\test_case\models\function.py

function.py

# __author__ = 'Ztiny'
#-*-coding:utf-8-*-

from selenium import webdriver
import os

#截圖函數
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.Ie()
    driver.get("http://www.baidu.com")
    insert_img(driver,'baidu.jpg')
    driver.quit()

  創建截圖函數insert_img(),為了保持自動化項目的移植性,采用相對路徑的方式將測試截圖保持到.\report\image目錄中。

三、編寫Page Object

  首先創建基礎Page基礎類(百度主頁為例):
  ...\mztestpro\bbs\test_case\page_obj\base.py

base.py

# __author__ = 'Ztiny'
#-*-coding:utf-8-*-

class Page(object):
    '''
    頁面基礎類,用於所有頁面的繼承
    '''

    baidu_url = 'https://www.baidu.com'

    def __init__(self,selenium_driver,base_url = baidu_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 find_element(self,*loc):
        return self.driver.find_element(*loc)

    def find_elements(self,*loc):
        return self.driver.find_elements(*loc)

    def open(self):
        self._open(self.url)

    def on_page(self):
        return (self.driver.current_url).encode('utf-8') == (self.base_url + self.url)
def script(self,src): return self.driver.execute_script(src)

   創建頁面基礎類,通過__init__()方法初始化參數:瀏覽器驅動、URL地址、超時時長等。定義基本方法:open()用於打開BBS地址:find_element()和find_elements()分別用來定位單個與多個元素;創建script()方法可以更簡便地調用JavaScript代碼。當然還可以對更多的WebDriver方法進行重定義。

   創建BBS登錄對象類:

   ...\mztestpro\bbs\test_case\page_obj\loginPage.py

loginPage.py

# __author__ = 'Ztiny'
# -*-coding:utf-8-*-

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 = '/'

    #Action
    baidu_login_user_loc = (By.LINK_TEXT,u'登錄')

    #彈出登錄窗口
    def baidu_login(self):
        self.find_element(*self.baidu_login_user_loc).click()

    login_username_loc = (By.ID,'TANGRAM__PSP_8__userName')
    login_password_loc = (By.ID,'TANGRAM__PSP_8__password')
    login_button_loc = (By.ID,'TANGRAM__PSP_8__submit')

    #登錄用戶名
    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="**********@qq.com", password="*********"):
        '''獲取用戶名和面登錄'''
        self.open()
        self.baidu_login()
        self.login_username(username)
        self.login_password(password)
        self.login_button()
        sleep(2)
user_error_hint_loc
= (By.LINK_TEXT,u"賬號不能為空") pawd_error_hint_loc = (By.LINK_TEXT,u"密碼不能為空") user_login_success_loc = (By.LINK_TEXT,u'Ztiny') #用戶名錯誤提示 def user_error_hint(self): return self.find_element(*self.user_error_hint_loc).text #密碼錯誤提示 def pawd_error_hint(self): return self.find_element(*self.pawd_error_hint_loc).text #登錄成功用戶名 def user_login_success(self):
return self.find_element(*self.user_login_success_loc).text

   創建登錄頁面對象,對用戶登錄頁面上的用戶名/密碼輸入框、登錄按鈕和提示信息等元素的定位進行封裝。除此之外,還創建user_login()方法作為系統統一登錄的入口。關於對操作步驟的封裝可以放在Page Object當中,也可以放在測試用例當中,這個主要根據具體的需求來衡量。這里之所以要放在Page Object當中,主要考慮到還會有其他的測試用例調用到該登錄方法。為username 和 password 入參數設置了默認值是為了方便其他用例在調用user_login()時不用再傳遞登錄用戶信息,因為該系統大多用例的執行使用該賬號即可,同時也方便了在賬號失效時的修改。

四、編寫測試用例

   現在開始編寫測試用程序,因為前面已經做好了基礎工作,此時測試用例的編寫將會簡單的許多,更能集中精力考慮用例的設計和事項。

   創建BBS登錄類:

   ...\mztestpro\bbs\test_case\login_sta.py

 此處需要注意文件名的創建。例如,假設登錄頁的對象命名為loginPage.py,那么關於測試登錄的用例文件應該命名為login_sta.py,這樣方便后期用例報錯時問題跟蹤。盡量把一個頁面上的元素定位封裝到一個“*Page.py”文件中,把針對這個頁面的測試用例集中到一個“*_sta.py”文件中

login_sta.py

# __author__ = 'Ztiny'
#-*-coding:utf-8-*-

from time import sleep
import unittest, random ,sys
sys.path.append("./models")
sys.path.append("./page_obj")
from models import myunit, function
from page_obj.loginPage import login

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.user_error_hint(),"賬號不能為空") self.assertEqual(po.pawd_error_hint()."密碼不能為空") function.insert_img(self.driver,"user_pawd_empty.jpg") def test_login2(self): '''用戶名正確,密碼為空登錄''' self.user_login_verify(username="*******") po = login(self.driver) self.assertEqual(po.pawd_error_hint(),"密碼不能為空") function.insert_img(self.driver,"paqd_empty.jpg") def test_login3(self): '''用戶名為空,密碼正確''' self.user_login_verify(password="*******") po = login(self.driver) self.assertEqual(po.user_error_hint(),"賬號不能為空") function.insert_img(self.driver,"user_empty.jpg") def test_login4(self): '''用戶名與密碼不匹配''' character = random.choice('abcdefghijklmnopqrstuvwxyz') username = "zhangsan" + character self.user_login_verify(username=username,password="123456") po = login(self.driver) self.assertEqual(po.pawd_error_hint(),"密碼與賬號不匹配") function.insert_img(self.driver,"user_pwad_error.jpg") def test_login5(self): '''用戶名、密碼正確''' self.user_login_verify(username='********@qq.com',password='********') sleep(2) po = login(self.driver) self.assertEqual(po.user_login_success(), u'Ztiny') function.insert_img(self.driver ,"user_pwd_ture.jpg") if __name__ == '__main__': unittest.main()

   首先創建loginTest()類,繼承myunit.Mytest()類,關於Mytest()類的實現,請翻看前面代碼。這樣就省去了在每一個測試類中實現一遍setUp()和tearDown()方法。

   創建user_login_verify()方法,並調用loginPage.py中定義的user_login()方法。為什么不直接調用呢?因為user_login()的入參已經設置了默認值,原因前面已經解釋,這里需要重新將其入參的默認值設置為空即可。

   前三條測試用例很好理解,分別驗證:

  • 用戶名密碼為空,點擊登錄
  • 用戶名正確,密碼為空,點擊登錄
  • 用戶名為空,密碼正確,點擊登錄   

 第四條用例驗證錯誤用戶名和密碼登錄。在當前系統中如果反復使用固定錯誤的用戶名和密碼,系統會彈出驗證碼輸入框。為了避免這種情況的發生,就需要用戶名進行隨機變化,此處的做法用固定前綴“zhangsan”,末尾字符從a~z中隨機一個字符與前綴進行拼接。

   第五條用例驗證正確的用戶名和密碼登錄,通過獲取用戶名作為斷言信息

   在上面的測試用例中,每條測試用例結束時都調用function.py文件中的insert_img函數進行截圖。當用例運行完成后,打開...\report\image\目錄將會看到用例執行的截圖文件,如圖:

五、執行測試用例

   為了在測試用例運行過程中不影響做其他事,筆者選擇調用遠程主機或虛擬機來運行測試用例,那么這里就需要使用Selenium Grid(其包含Selenium Server)來調用遠程節點。

   創建...\mztestpro\startup.bat文件,用於啟動...\mztestpro\driver\目錄下的Selenium Server。

startup.bat

@echo off
echo 啟動hub
java -jar .\mztestpro\driver\selenium-server-standalone-2.40.0.jar -role hub -host 192.168.0.102 -port 4444
echo 啟動node
java -jar .\mztestpro\driver\selenium-server-standalone-2.40.0.jar -role node -port 5555 -hub  http://192.168.0.102:4444/grid/register

 

   雙擊startup.bat文件,啟動Selenium Server創建主hub節點。在遠程主機或虛擬機中通樣需要啟動Selenium Server創建node節點。

   創建用例執行程序:...\mztestpro\run_bbs_test.py

run_bbs_test.py

# __author__ = 'Ztiny'
#-*-coding:utf-8-*-

from HTMLTestRunner import HTMLTestRunner
from email.mime.text import MIMEText
import smtplib
import unittest
import time
import os
# =========================郵件接收者============================
mailto_list=["********@qq.com"]
#============= 設置服務器,用戶名、口令以及郵箱的后綴===============
mail_host="smtp.163.com"
mail_user="******@163.com"
mail_pass="*******"
#===========================發送郵件============================
def send_mail(to_list,file_new):
    '''''
    to_list:發給誰
    sub:主題
    content:內容
    send_mail("aaa@126.com","sub","content")
    '''
    f = open(file_new, 'rb')
    mail_body = f.read()
    f.close()
    me=mail_user
    msg = MIMEText(mail_body,'html','utf-8')
    msg['Subject'] = u'自動化測試報告'
    msg['From'] = me
    msg['To'] = ";".join(to_list)
    try:
        s = smtplib.SMTP()
        s.connect(mail_host,25)
        s.login(mail_user,mail_pass)
        s.sendmail(me, to_list, msg.as_string())
        s.close()
        return True
    except Exception, e:
        print str(e)
        return False

#==============查找測試報告目錄,找到最新生成的測試報告文件==========
def new_report(testreport):
    lists = os.listdir(testreport)
    lists.sort(key=lambda fn:os.path.getatime(testreport + "\\" + fn))
    file_new = os.path.join(testreport,lists[-1])
    print(file_new)
    return file_new

if __name__ == '__main__':
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    filename = './bbs/report/' + now +'result.html'
    fp = open(filename,'wb')
    runner = HTMLTestRunner(stream=fp,
                            title=u'百度登錄自動化測試報告',
                            description=u'環境 :window 7 瀏覽器:firefox')
    discover = unittest.defaultTestLoader.discover('./bbs/test_case',
                                                   pattern='*_sta.py')
    runner.run(discover)
    fp.close()
    file_path = new_report('./bbs/report/')

    if send_mail(mailto_list,file_path):
        print u"發送成功"
    else:
        print u"發送失敗"

 

   執行過程中並沒有任何改動,集成HTMLTestRunner生成的HTML測試報告,以及集成自動發郵件功能等。唯一需要注意的是,腳本中的路徑建議使用相對路徑,以便於項目被移動到任意目錄下執行。

   打開...\mztestpro\driver.py 文件,修改腳本運行的節點及瀏覽器。現在可以通過運行run_bbs_test.py來執行測試項目了。

小結:

   如果你完成了前面的操作,那么這只是自動化項目的開始,不過,我們已經把基本架構設計完成,后面大部分工作就是編寫各個頁面的*Page.py以及測試用*_sta.py。在這個過程中會遇到各種各樣的問題,如元素定位、架構的擴展,需要讀者自己去克服這些問題。

PS:對自動化感興趣的同學可以加QQ群:539725853,歡迎加入交流!

轉載請注明出處:http://www.cnblogs.com/xiaozhiblog/p/5378723.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM