Python Web自動化測試

1、基礎準備

1. 環境搭建

 工欲善其事必先利其器,廢話很少說。咱們先開始搭建環境。javascript

# 建立項目目錄
mkdir WebTesting

# 切換到項目目錄下
cd WebTesting

# 安裝虛擬環境建立工具
pip install virtualenv

# 建立虛擬環境,env表明虛擬環境的名稱,可自行定義
virtualenv env

# 啓動虛擬環境,執行下面命令後會發現路徑上有 (env) 字樣的標識
source env/Scripts/activate

# 查看 (env) 環境下使用的 Python 和 pip 工具版本
ls env/Scripts/

# *** 安裝 Selenium ***
pip install selenium

# 退出虛擬環境,退出後路徑上的 (env) 字樣的標識消失
cd env/Scripts/
deactivate

# 導出環境所須要的模塊的清單
pip freeze >> requirements.txt

# 上傳 GitHub 時,將下面項忽略上傳
echo env/ >> .gitignore
echo WebTesting.iml >> .gitignore
echo __pycache__/ >> .gitignore

# 將代碼傳至 GitHub
# 本地倉初始化
git init
# 建立本地倉與 GitHub 倉的遠程連接
git remote add github 你的github倉的地址
# 將代碼添加到暫存區
git add .
# 將代碼提交到 
git commit -m "init environment"
# 將代碼上傳到GitHub倉中
git push github master

複製代碼

初始化環境的項目結構示例以下: css

初始化環境項目結構示例

2. Selenium 原理

   Selenium 是一套完整的 web 應用程序測試系統 ,它包含了測試錄製(Selenium IDE)、編寫及運行(Selenium Remote Control) 和測試的並行處理(Selenium Grid)。Selenium的核心 Selenium Core基於 JsUnit,徹底由 JavaScript 編寫,所以能夠運行於任何支持 JavaScript 的瀏覽器上。其基本原理以下: html

selenium工做原理圖

2.1 設置瀏覽器驅動

from selenium import webdriver

driver = webdriver.Firefox()    # Firefox瀏覽器
driver = webdriver.Chrome()     # Chrome瀏覽器
driver = webdriver.Ie()         # Ie瀏覽器
driver = webdriver.Edge()       # Edge瀏覽器
driver = webdriver.PhantomJS()  # PhantomJS()
複製代碼

2.2 Selenium元素定位

<html>
  <head>
  <body link="#0000cc">
    <a id="result_logo" href="/" onmousedown="return c({'fm':'tab','tab':'logo'})">
    <form id="form" class="fm" name="f" action="/s">
      <span class="soutu-btn"></span>
        <input id="kw" class="s_ipt" name="wd" value="" maxlength="255" autocomplete="off">
複製代碼
# 經過 id 定位
dr.find_element_by_id("kw")

# 經過name定位:
dr.find_element_by_name("wd")

# 經過class name定位:
dr.find_element_by_class_name("s_ipt")

# 經過tag name定位:
dr.find_element_by_tag_name("input")

# 經過 xpath 定位的幾種寫法
dr.find_element_by_xpath("//*[@id='kw']")
dr.find_element_by_xpath("//*[@name='wd']")
dr.find_element_by_xpath("//input[@class='s_ipt']")
dr.find_element_by_xpath("/html/body/form/span/input")
dr.find_element_by_xpath("//span[@class='soutu-btn']/input")
dr.find_element_by_xpath("//form[@id='form']/span/input")
dr.find_element_by_xpath("//input[@id='kw' and @name='wd']")

# 經過 css 定位的幾種寫法
dr.find_element_by_css_selector("#kw")
dr.find_element_by_css_selector("[name=wd]")
dr.find_element_by_css_selector(".s_ipt")
dr.find_element_by_css_selector("html > body > form > span > input")
dr.find_element_by_css_selector("span.soutu-btn> input#kw")
dr.find_element_by_css_selector("form#form > span > input")

# 經過 link_text 定位
dr.find_element_by_link_text("新聞")
dr.find_element_by_link_text("hao123")
dr.find_element_by_partial_link_text("新")
dr.find_element_by_partial_link_text("hao")
dr.find_element_by_partial_link_text("123")

# 若是是定位一組元素,用下面
find_elements_by_id()
find_elements_by_name()
find_elements_by_class_name()
find_elements_by_tag_name()
find_elements_by_link_text()
find_elements_by_partial_link_text()
find_elements_by_xpath()
find_elements_by_css_selector()
複製代碼

2.3 控制瀏覽器操做

(1) 控制瀏覽器窗口大小

WebDriver中 set_window_size() 方法來設置瀏覽器窗口的大小;maximize_window() 使打開的瀏覽器全屏顯示。 【GitHub示例html5

from selenium import Webdriver

driver = Webdriver.Chrome('../tools/chromedriver.exe')
driver.get_url('http://www.5itest.cn/register')

# 設置瀏覽器窗口大小
print("設置瀏覽器寬500,高600")
driver.set_window_size()
driver.quit()
複製代碼

(2) 控制瀏覽器後退、前進

webdriver 提供了對應的 back() 和 forward() 方法來模擬後退和前進按鈕。【GitHub示例java

from selenium import webdriver
import time


# 2. 控制瀏覽器的前進、後退
browser_links = webdriver.Chrome('../tools/chromedriver.exe')
first_url = 'https://www.baidu.com/'
second_url = 'https://news.baidu.com/'

print("訪問第一個連接:%s" % first_url)
browser_links.get(first_url)

time.sleep(1)
print("訪問第二個連接:%s" % second_url)
browser_links.get(second_url)

time.sleep(1)
print("回退到第一個連接:%s" % first_url)
browser_links.back()

time.sleep(1)
print("前進到第二個連接:%s", second_url)
browser_links.forward()

time.sleep(1)
browser_links.quit()
複製代碼

(3) 刷新頁面 F5

webdriver中能夠用 refresh 方法進行頁面刷新。【GitHub代碼node

from selenium import webdriver
import time

refresh_url = 'http://www.baidu.com/'
browser_refresh = webdriver.Chrome('../tools/chromedriver.exe')
browser_refresh.get(refresh_url)
time.sleep(2)
browser_refresh.refresh()
browser_refresh.quit()
複製代碼

2.4 webdriver經常使用方法

webdriver經常使用方法【GitHub示例python

(1) 點擊、輸入和清除

定位元素後咱們還須要對元素進行操做,經常使用的元素操做方法有:clear()、send_keys(value)、click()git

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 03_commonMethod.py @Time : 2019/8/20 12:12 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
import time

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

browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 1. 清除、輸入、點擊
browser.find_element_by_id('kw').clear()
browser.find_element_by_id('kw').send_keys('python')
browser.find_element_by_id('su').click()
time.sleep(2)
browser.quit()

複製代碼

(2) 提交

submit()方法用於提交表單,在搜索框後輸入關鍵字後,可用於「回車」模擬。github

from selenium import webdriver
import time

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

browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 2.提交
search_text = browser.find_element_by_id('kw')
search_text.send_keys('selenium')
search_text.submit()
time.sleep(3)
複製代碼

(3) 其餘經常使用的方法

size: 返回元素的尺寸。
text: 獲取元素的文本。
get_attribute(name): 得到屬性值。
is_displayed(): 設置該元素是否用戶可見。
複製代碼
from selenium import webdriver
import time

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

browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 3. 其餘經常使用方法
size = browser.find_element_by_id('kw').size
print("返回元素的尺寸:%s" % size)

text = browser.find_element_by_id('cp').text
print("返回元素的文本:%s" % text)

attribute = browser.find_element_by_id('kw').get_attribute('type')
print("返回元素的屬性:%s" % attribute)

result = browser.find_element_by_id('kw').is_displayed()
print("返回元素是否可見:%s" % result)

browser.quit()
複製代碼

2.4 鼠標事件

在webdriver中,鼠標操做的方法封裝在 ActionChains 類提供。ActionChains類提供了鼠標操做的經常使用方法:【GitHub示例web

ActionChains(driver),將瀏覽器驅動 driver 做爲參數傳入。
(1) perform(): 執行全部 ActionChains 中存儲的行爲,是對整個操做的提交動做;
(2) context_click(): 右擊
(3) double_click(): 雙擊
(4) drag_and_drop(): 拖動
(5) move_to_element(): 鼠標懸停, 在調用時須要指定元素定位
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 04_mouseEvent.py @Time : 2019/8/20 16:59 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time


base_url = 'http://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 定位到懸停元素處
above = browser.find_element_by_link_text('設置')
# 對元素執行鼠標懸停操做
ActionChains(browser).move_to_element(above).perform()
time.sleep(5)

# 右擊
ActionChains(browser).context_click().perform()
time.sleep(5)

# 定位到要雙擊的元素處
# double_click_element = browser.find_element_by_link_text('新聞')
# print(double_click_element)
# ActionChains(browser).move_to_element(double_click_element).double_click().perform()
# time.sleep(5)

# 拖動元素
drag_and_drop_element = browser.find_element_by_link_text('地圖')
ActionChains(browser).move_to_element(drag_and_drop_element).drag_and_drop().perform()
time.sleep(5)

browser.quit()

複製代碼

2.5 鍵盤事件

  前面的 send_keys() 方法用來模擬鍵盤輸入;keys() 類提供了鍵盤上幾乎全部按鍵的方法,組合鍵也是能夠的。【GitHub示例

經常使用的鍵盤操做以下:

send_keys(Keys.BACK_SPACE) 刪除鍵(BackSpace)
send_keys(Keys.SPACE) 空格鍵(Space)
send_keys(Keys.TAB) 製表鍵(Tab)
send_keys(Keys.ESCAPE) 回退鍵(Esc)
send_keys(Keys.ENTER) 回車鍵(Enter)
send_keys(Keys.CONTROL,'a') 全選(Ctrl+A)
send_keys(Keys.CONTROL,'c') 複製(Ctrl+C)
send_keys(Keys.CONTROL,'x') 剪切(Ctrl+X)
send_keys(Keys.CONTROL,'v') 粘貼(Ctrl+V)
send_keys(Keys.F1) 鍵盤 F1
……
send_keys(Keys.F12) 鍵盤 F12
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 05_keyboardEvent.py @Time : 2019/8/20 22:22 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time


base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(base_url)


# 先輸入百度
driver.find_element_by_id('kw').send_keys('百度')
# 1.刪除度
driver.find_element_by_id('kw').send_keys(Keys.BACK_SPACE)
time.sleep(3)

# 2.鍵入空格
driver.find_element_by_id('kw').send_keys(Keys.SPACE)
driver.find_element_by_id('kw').send_keys('加入空格')
time.sleep(5)

# 3.ctrl+a 全選輸入框裏的內容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL, 'a')
time.sleep(3)

# 4.ctrl+x 剪切輸入框裏的內容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL, 'x')
time.sleep(3)

# 5. ctrl+v 粘貼剪切的內容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL, 'v')
time.sleep(3)

# 6. 回車
driver.find_element_by_id('su').send_keys(Keys.ENTER)
time.sleep(3)


driver.quit()
複製代碼

2.6 獲取斷言信息

  測試時須要拿實際結果與預期結果進行比較,這個比較稱爲 斷言,一般能夠獲取斷言元素有:【GitHub示例

(1)title:當前頁面的標題
(2)current_url:當前頁面的URL
(3)text:獲取元素的文本信息
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 06_asseert.py @Time : 2019/8/20 23:23 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
import time


base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(base_url)

time.sleep(1)

print("直接訪問連接後頁面元素獲取")
title = driver.title
print('first title: %s' % title)
current_url = driver.current_url
print('first current_url: %s' % current_url)


driver.find_element_by_id('kw').send_keys('python')
driver.find_element_by_id('su').click()
time.sleep(1)
print('搜索關鍵詞後頁面元素獲取')
title2 = driver.title
print('second title: %s' % title2)
current_url2 = driver.current_url
print('first current_url2: %s ' % current_url2)
kw_text = driver.find_element_by_id('kw').text
print('nums text: %s' % kw_text)


driver.quit()

複製代碼

2.7 設置元素等待

  webdriver提供了兩種等待方式:顯示等待隱式等待

(1) 顯示等待使webdrver 等待某條件成立時繼續執行,不然在達到最大時長時拋出超時異常(TimeoutException)。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 07_wait.py @Time : 2019/8/21 14:01 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

base_url = 'http://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(base_url)


# 1.顯示等待
# WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
# driver :瀏覽器驅動。
# timeout :最長超時時間,默認以秒爲單位。
# poll_frequency :檢測的間隔(步長)時間,默認爲0.5S。
# ignored_exceptions :超時後的異常信息,默認狀況下拋NoSuchElementException異常
# until(method, message=‘’)-----調用該方法提供的驅動程序做爲一個參數,直到返回值爲True。
# until_not(method, message=‘’)---調用該方法提供的驅動程序做爲一個參數,直到返回值爲False。
# presence_of_element_located()方法判斷元素是否存在。
element = WebDriverWait(driver, 5, 0.5).until(
    EC.presence_of_element_located((By.ID, 'kw'))
    )
element.send_keys('要搜索的內容')
time.sleep(3)

driver.quit()
複製代碼

(2) 隱式等待:WebDriver提供了implicitly_wait()方法實現隱式等待,默認設置爲0。假設在第6秒定位到了元素則繼續執行,若直到超出設置時長(10秒)尚未定位到元素,則拋出異常。它的設置並不影響程序的執行速度。【GitHub示例

# 2. 隱式等待
from selenium.common.exceptions import NoSuchElementException
from selenium import webdriver
from time import ctime
import time

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

browser = webdriver.Chrome('../tools/chromedriver.exe')

# 設置隱式等待爲10s
browser.implicitly_wait(10)
browser.get(base_url2)

try:
    print(ctime())
    browser.find_element_by_id('kw').send_keys('se')
    time.sleep(3)
except NoSuchElementException as e:
    print(e)
finally:
    print(ctime())
    browser.quit()

複製代碼

2.8 多窗口切換

  在頁面操做過程當中有時候點擊某個連接會彈出新的窗口,這時就須要主機切換到新打開的窗口上進行操做。webdriver 中的 switch_to.window() 方法,能夠實如今不一樣窗口之間切換。【GitHub示例

current_window_handle:得到當前窗口句柄
window_handles:返回全部窗口的句柄到當前會話
switc_to.window():用於切換到相應的窗口
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 08_switchWindow.py @Time : 2019/8/22 21:48 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
import time

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


browser = webdriver.Chrome('../tools/chromedriver.exe')
# 隱式等待10秒
browser.implicitly_wait(10)
browser.get(base_url)


# 得到搜索窗口的句柄
search_windows = browser.current_window_handle
browser.find_element_by_link_text('登陸').click()
browser.find_element_by_link_text('當即註冊').click()

# 活得當前打開窗口的句柄
all_handles = browser.window_handles

# 進入註冊窗口
for handle in all_handles:
    if handle != search_windows:
        browser.switch_to.window(handle)
        print('now register window!')
        browser.find_element_by_name('account').send_keys('username')
        browser.find_element_by_name('password').send_keys('password')
        time.sleep(2)

browser.quit()
複製代碼

2.9 警告框處理

  在 webdriver 中處理 JavaScript 所生成的 alert、confirm 以及 prompt 十分簡單,具體作法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,而後使用 text/accept/dismiss/ send_keys 等方法進行操做。【GitHub示例

text:返回 alert/confirm/prompt 中的文字信息。
accept():接受現有警告框。
dismiss():解散現有警告框。
send_keys(keysToSend):發送文本至警告框。keysToSend:將文本發送至警告框。
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 09_alert.py @Time : 2019/8/22 22:30 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time


base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.implicitly_wait(10)
driver.get(base_url)

# 鼠標懸停至 「設置」 連接
link = driver.find_element_by_link_text('設置')
ActionChains(driver).move_to_element(link).perform()

# 打開搜索設置
driver.find_element_by_link_text('搜索設置').click()
time.sleep(3)

# 點擊 「搜索設置」
driver.find_element_by_class_name('prefpanelgo').click()
time.sleep(3)

# 接受警告框prefpanelgo
driver.switch_to.alert.accept()
time.sleep(3)

driver.quit()
複製代碼

2.10 下拉框選擇

  webdriver 提供了 Select 類來處理下拉框。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 10_select.py @Time : 2019/8/22 22:49 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep

base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.implicitly_wait(10)
driver.get(base_url)

# 鼠標懸停至「設置」連接
driver.find_element_by_name('設置').click()
sleep(2)
# 打開 「搜索設置」
driver.find_element_by_name('搜索設置').click()
sleep(2)
# 搜索結果顯示條數
# Select類用於定位select標籤。
sel = driver.find_element_by_xpath("//select[@id='nr']")
# select_by_value() 方法用於定位下接選項中的value值。
Select(sel).select_by_value('50')

driver.quit()
複製代碼

2.11 文件上傳

  經過input標籤實現的上傳功能,能夠將其看做是一個輸入框,即經過send_keys()指定本地文件路徑的方式實現文件上傳。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 11_upfile.py @Time : 2019/8/23 0:12 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
import os


driver = webdriver.Chrome('../tools/chromedriver.exe')
file_path = "file:///" + os.path.abspath('upfile.html')
driver.get(file_path)

# 定位上傳按鈕的位置
driver.find_element_by_name('file').send_keys(os.path.abspath('upfile.txt'))
driver.quit()
複製代碼

2.12 cookie操做

  網站爲了辨別用戶身份、進行 session 跟蹤而存儲在用戶本地終端上的數據,也能夠叫作瀏覽器緩存。webdriver 對 cookie 的經常使用操做有添加、刪除、讀取。【GitHub示例

(1) get_cookies()-----得到全部的 cookie 信息
(2) get_cookie(name)-----活得 key 值爲 name 的 cookie 的信息
(3) add_cookie(cookie_dict)----添加 cookie。"cookie_dict" 指字典對象,必須有 name 和 value 值
(4) delete_cookie(name,optionsString):刪除cookie信息。「name」是要刪除的cookie的名稱,「optionsString」是該cookie的選項,目前支持的選項包括「路徑」,「域」
(5) delete_all_cookies()----刪除全部 cookie 信息
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 12_cookie.py @Time : 2019/8/23 10:11 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """
from selenium import webdriver
from time import sleep


base_url = 'https://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 1. 獲取 cookie 信息
cookies = browser.get_cookies()
print(cookies)
sleep(2)
browser.quit()

# 2. cookie 寫入
browser.add_cookie(
    {
        'name': 'add-cookie',
        'value': 'add-cookie-value'
    }
)
# 遍歷cookies打印cookie信息
for cookie in browser.get_cookies():
    print("%s ---> %s" % (cookie['name'], cookie['value']))
sleep(2)
browser.quit()
複製代碼

2.13 調用 JavaScript

  對於 webdriver 中沒法操做的動做(例如:滾動瀏覽器的側邊欄),能夠調用 webdriver 進行瀏覽器的控制。webdriver 提供了execute_script()方法來執行 JavaScript 代碼。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 13_callJavaScript.py @Time : 2019/8/24 12:46 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from time import sleep


base_url = 'https://www.baidu.com'
browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# window.scrollTo()方法用於設置瀏覽器窗口滾動條的水平和垂直位置。方法的第一個參數表示水平的左間距,第二個參數表示垂直的上邊距。
browser.set_window_size(500, 500)
browser.find_element_by_id('kw').send_keys('百度')
browser.find_element_by_id('su').click()
sleep(2)

# 經過javascript設置瀏覽器窗口的滾動條位置
js = "window.scrollTo(100, 450);"
browser.execute_script(js)
sleep(2)

browser.quit()

複製代碼

2.14 窗口截圖

  自動化用例是由程序去執行的,所以有時候打印的錯誤信息並不十分明確。若是在腳本執行出錯的時候能對當前窗口截圖保存,那麼經過圖片就能夠很是直觀地看出出錯的緣由。webdriver 提供了截圖函數 get_screenshot_as_file() 來截取當前窗口。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : 14_screenShoot.py @Time : 2019/8/24 13:00 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from time import sleep

base_url = 'http://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')

browser.get(base_url)

browser.find_element_by_id('kw').send_keys('python selenium')
browser.find_element_by_id('su').click()
sleep(2)

# 截取當前窗口並指定報錯截圖的位置
# browser.get_screenshot_as_file('ScreenShot/14_screenShot.jpg')
browser.get_screenshot_as_file('ScreenShot/14_screenShot.png')

browser.quit()
複製代碼

2.15 關閉瀏覽器

  關於瀏覽器的關閉:

close() 關閉單個窗口
quit() 關閉全部窗口
複製代碼

2、通用模型

  爲了便於自動化測試的學習,使用網站 樂學 http://www.5itest.cn/ 做爲測試對象。

1. 啓動測試頁面

  全局定義測試頁面地址 ---> 建立瀏覽器驅動對象 ---> 瀏覽器對象打開測試頁面 ---> 頁面加載等待 ---> 判斷測試頁面是否被打開 ---> 關閉測試頁面,退出瀏覽器【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register.py @Time : 2019/8/25 9:42 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from time import sleep


# 1.啓動要測試頁面
# 全局定義註冊頁面地址
register_url = 'http://www.5itest.cn/register'
# 建立 driver 對象
driver = webdriver.Chrome('../tools/chromedriver.exe')
# 打開測試頁面
driver.get(register_url)
# 等待頁面加載
sleep(3)
# 經過title是否加載成功,來判斷註冊頁面是否加載成功了,下面的打印結果說明了該對象存在於內存對象中也就是title加載成功了
print(EC.title_contains('註冊'))


# 關閉瀏覽器(close關閉單個頁面)
driver.close()

複製代碼

2. 頁面元素查找

  經過 id(class/xpath等) 查找註冊頁面元素。【GitHub示例

測試註冊頁面

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


# 2.查找頁面元素並傳值給對應的元素處
# 郵箱地址
register_email = driver.find_element_by_id('register_email')
print("郵箱地址的提示語:", register_email.get_attribute('placeholder'))
register_email.send_keys('register_mail1@163.com')
# 用戶名
register_nickname = driver.find_element_by_id('register_nickname')
print("用戶名的提示語:", register_nickname.get_attribute('placeholder'))
register_nickname.send_keys('register_mail1')
# 密碼
register_password = driver.find_element_by_id('register_password')
print("密碼的提示語:", register_password.get_attribute('placeholder'))
register_password.send_keys('test@password')
# 驗證碼
captcha_code = driver.find_element_by_xpath('//*[@id="captcha_code"]')
print('驗證碼的提示語:', captcha_code.get_attribute('placeholder'))
captcha_code.send_keys('x7xx4')
# 檢查《用戶協議》是否加載出來了來判斷
user_terms = driver.find_element_by_id('user_terms')
locator = (By.ID, 'user_terms')
print(WebDriverWait(driver, 1).until(EC.visibility_of_element_located(locator)))


# 關閉瀏覽器(close關閉單個頁面)
driver.close()
複製代碼

3. 生成測試數據

  註冊時咱們一般須要大量的測試數據(例如註冊頁面的測試),這時候咱們就須要想辦法用代碼生成大量的測試數據(如郵箱、用戶名等)。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : generate_testdata.py @Time : 2019/8/25 21:38 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import random

# 1. 生成測試郵箱數據
for i in range(5):
    register_test_email = ''.join(random.sample('abcdefgh1234567890', 8)) + '@163.com'
    print("生成的五組測試郵箱帳號爲:", register_test_email)


# 2. 同理,生成測試暱稱數據
for i in range(5):
    register_nickname = ''.join(random.sample('abcdefghijk', 5))
    print("生成的五組測試用戶暱稱爲:", register_nickname)

複製代碼

4. 圖片驗證碼破解

  針對註冊頁面的圖片驗證碼,咱們怎麼解決呢?最簡單的就是在代碼中註釋掉驗證碼的代碼,可是這種不太友好,不符合普通用戶的使用場景。

  那麼怎麼識別圖片驗證碼中的文字呢?

(1)將整個註冊頁面保存下來
(2)定位圖片驗證碼圖片的座標
(3)計算圖片四個定點的位置
(4)將圖片驗證截取
(5)利用第三方庫或者專門的圖片驗證碼識別接口進行圖片驗證碼文字的識別
複製代碼

4.1 定位驗證碼圖片

  先進行驗證碼的圖片的定位【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : position_captchacode.py @Time : 2019/8/26 12:28 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from PIL import Image
from selenium import webdriver
from time import sleep

register_url = 'http://www.5itest.cn/register'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(register_url)
sleep(3)

# (1)將含有驗證碼的頁面截圖保存下來(這裏指的是註冊頁面)
driver.save_screenshot('image/register_screenshot.png')

# (2)定位圖片驗證碼圖片的座標
code_element = driver.find_element_by_id('getcode_num')
print("驗證碼的圖片左上角頂點的座標爲:", code_element.location)
print("驗證碼的圖片高寬的大小爲:", code_element.size)

# (3)計算圖片四個定點的位置
left = code_element.location['x']
top = code_element.location['y']
right = code_element.size['width'] + left
height = code_element.size['height'] + top
image = Image.open('image/register_screenshot.png')

# (4)將圖片驗證截取
code_image = image.crop((left, top, right, height))
code_image.save('image/captchcode_image.png')


driver.close()
複製代碼

4.2 識別圖片驗證碼

  獲取到了圖片驗證碼後,能夠調用第三方接口(www.showapi.com/api/lookPoi… Python 也有第三方庫(pytesseract)能夠識別圖片驗證碼,可是隻能識別簡單的數字圖片,對於組合複雜的圖片就力不從心了。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : discern_codeimagepy @Time : 2019/8/26 18:17 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : ShowapiRequest庫從https://www.showapi.com/api/lookPoint/184下載 """

from selenium import webdriver
from time import sleep
from api import ShowapiRequest

register_url = 'http://www.5itest.cn/register'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(register_url)
sleep(5)
captcha_code = driver.find_element_by_xpath('//*[@id="captcha_code"]')
print('驗證碼的提示語:', captcha_code.get_attribute('placeholder'))
sleep(5)

# 解析驗證碼圖片中的文字(用第三方的圖片驗證碼識別接口 ShowApiRequest)
# 這裏my_appId須要替換成你本身的my_appId,my_appSecret須要替換成你本身的my_appSecret
r = ShowapiRequest("http://route.showapi.com/184-4", "my_appId", "my_appSecret")
r.addBodyPara("img_base64", "")
r.addBodyPara("typeId", "35")
r.addBodyPara("convert_to_jpg", "0")
r.addBodyPara("needMorePrecise", "0")
r.addFilePara("image", r"image/captchcode_image.png")  # 文件上傳時設置
res = r.post()
text = res.json()["showapi_res_body"]["Result"]
captcha_code.send_keys(text)
sleep(3)

driver.close()
複製代碼

5. 讀取元素配置文件

  通過前面的學習,有沒有感受到咱們寫了大量的重複代碼。例如:頁面中關鍵元素的查找與傳值。對於一個自動化測試頁面來講頁面的元素是相對於比較穩定的,那麼咱們就能夠把這些重複性的代碼封裝起來,直接去讀取頁面中相對於穩定的元素就能夠了,這些元素信息咱們能夠卸載配置文件中,當頁面元素髮生變更時,只須要修改相應的頁面元素信息便可,不用每次去修改代碼,從而儘量的避免引入bug。

5.1 建立配置文件

  讀取配置文件前,按頁面元素的特色寫進配置文件中。【GitHub示例

[RegisterElement]
register_email = id>register_email
register_nickname = id>register_nickname
register_password = id>register_password
getcode_num = id>getcode_num
captcha_code = id>captcha_code
複製代碼

5.2 讀取配置文件

GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : read_ini.py @Time : 2019/8/27 12:48 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import configparser


class ReadIni(object):
    # 初始化配置文件路徑及節點加載
    def __init__(self, file_name=None, node=None):
        if file_name is None:
            self.file_name = '../data/RegisterElement.ini'
        if node is None:
            self.node = 'RegisterElement'
        else:
            self.node = node

        self.cf = self.load_ini()

    # 加載配置文件
    def load_ini(self):
        cf = configparser.ConfigParser()
        cf.read(self.file_name)
        return cf

    # 獲取各個配置項的值
    def get_value(self, key):
        data = self.cf.get(self.node, key)
        return data


if __name__ == "__main__":
    ri = ReadIni()
    print(ri.get_value('register_nickname'))

複製代碼

6. 查找元素封裝

  查找頁面元素代碼封裝。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : find_element.py @Time : 2019/8/27 14:45 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : 將頁面查找元素的功能封裝 """

from util.read_ini import ReadIni


class FindElement(object):
    def __init__(self, driver):
        self.driver = driver

    def get_element(self, key):
        ri = ReadIni()
        data = ri.get_value(key=key)
        by = data.split('>')[0]
        value = data.split('>')[1]
        try:
            if by == 'id':
                return self.driver.find_element_by_id(value)
            elif by == 'name':
                return self.driver.find_element_by_name(value)
            elif by == 'className':
                return self.driver.find_element_by_className(value)
            else:
                return self.driver.find_element_by_xpath(value)
        except:
            file_path = '../image/no_element.png'
            self.driver.save_screenshot(file_path)


if __name__ == "__main__":
    fe = FindElement()
    fe.get_element('register_nickname')
複製代碼

7. 代碼封裝

  寫到這裏是否是感受代碼雖然寫出來了,可是很沒有條理性。是的,如今就把代碼模塊化,讓其每一個模塊作一部分事情。【GitHub示例

代碼結構圖

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : package.py @Time : 2019/8/27 14:13 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from selenium import webdriver
from time import sleep
from util.read_ini import ReadIni
from basic.find_element import FindElement
import random
from PIL import Image
from api import ShowapiRequest


class Register(object):
    def __init__(self, url):
        self.driver = self.get_driver(url=url)

    # 啓動瀏覽器,打開目標測試頁面url
    def get_driver(self, url):
        driver = webdriver.Chrome('../tools/chromedriver.exe')
        driver.get(url=url)
        driver.maximize_window()
        return driver

    # 定位用戶信息,獲取元素element
    def get_user_element(self, key):
        find_element = FindElement(self.driver)
        user_element = find_element.get_element(key=key)
        return user_element

    # 輸入用戶信息
    def send_user_info(self, key, data):
        self.get_user_element(key=key).send_keys(data)

    # 獲取隨機數
    def get_range(self):
        number = ''.join(random.sample('abcdefg123456', 8))
        return number

    # 獲取驗證碼圖片
    def get_captcha_image(self, file_name):
        self.driver.save_screenshot(filename=file_name)
        captcha_element = self.get_user_element('getcode_num')
        left = captcha_element.location['x']
        top = captcha_element.location['y']
        right = captcha_element.size['width'] + left
        height = captcha_element.size['height'] + top
        image = Image.open(file_name)
        img = image.crop((left, top, right, height))
        img.save(file_name)

    # 識別圖片驗證碼
    def discern_captcha_image(self, file_name):
        self.get_captcha_image(file_name=file_name)
        # 解析驗證碼圖片中的文字(用第三方的圖片驗證碼識別接口 ShowApiRequest)
        r = ShowapiRequest("http://route.showapi.com/184-4", "48120", "12c017278c0845c2bcda177212d2d2ac")
        r.addBodyPara("img_base64", "")
        r.addBodyPara("typeId", "35")
        r.addBodyPara("convert_to_jpg", "0")
        r.addBodyPara("needMorePrecise", "0")
        r.addFilePara("image", file_name)  # 文件上傳時設置
        res = r.post()
        text = res.json()["showapi_res_body"]["Result"]
        return text

    # 主函數
    def main(self):
        register_nickname = self.get_range()
        register_email = self.get_range() + '@163.com'
        register_password = self.get_range() + '@123'
        file_name = '../image/code_image.png'
        captcha_code = self.discern_captcha_image(file_name=file_name)
        self.send_user_info('register_nickname', register_nickname)
        self.send_user_info('register_email', register_email)
        self.send_user_info('register_password', register_password)
        self.send_user_info('captcha_code', captcha_code)
        self.get_user_element('register-btn').click()
        sleep(5)
        self.driver.close()


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    r = Register(register_url)
    r.main()
複製代碼

7.1 異常處理

  測試中咱們要讓程序暢通運行的同時,也要注意對程序的異常進行處理。例如:因爲調用的第三方接口來是被驗證碼圖片,不免會出現識別不了的狀況,爲了避免阻塞程序的正常運行,當識別出錯的時候,能夠對錯誤識別進行異常的處理。【GitHub示例

# 在 [RegisterElement.ini] 文件添加驗證碼錯誤的元素id
captcha_code_error = id>captcha_code-error
複製代碼
# 在 package.py 代碼的基礎上加上異常處理
# 異常處理:註冊失敗進行截圖,方便問題排查
        captcha_code_error = self.get_user_element('captcha_code_error')
        if captcha_code_error is None:
            print("......恭喜你註冊成功了......")
        else:
            self.driver.save_screenshot('../image/captcha_code_error.png')
        sleep(5)
複製代碼

7.2 兼容多瀏覽器執行

  在實際的測試中,因爲不一樣瀏覽器的技術標準的差別,致使同一份代碼在不一樣瀏覽器中的表現形式不一致,因此避免不了多瀏覽器執行測試用例。【GitHub示例

注意:FireFox 驅動與 Chrome 的驅動配置不一樣

下載地址:https://github.com/mozilla/geckodriver/releases 
下載後(根據系統版本選擇):
    (1)解壓取出geckodriver.exe(以64x爲例);
    (2)將geckodriver.exe放到Firefox的安裝目錄下,如:(D:\火狐\Mozilla Firefox);
    (3)將火狐安裝目錄(D:\火狐\Mozilla Firefox)添加到環境變量path中;
    (4)重啓 IDEA,記得必定要
複製代碼
# 兼容多瀏覽器執行測試
    def get_more_driver(self, url, browser):
        if browser == 'chrome':
            # 版本 76.0.3809.100(64位)對應的驅動
            driver = webdriver.Chrome('../tools/chromedriver.exe')
        elif browser == 'firefox':
            # FireFox 68.0.2(64位) 對應的驅動,和 chrome 驅動使用有差別
            driver = webdriver.Firefox()
        driver.get(url=url)
        driver.maximize_window()
        return driver
        
複製代碼

8. 日誌記錄

  到這裏已經搞了好多,可是在排查問題的時候,不是很方便,咱們須要對程序的執行中錯誤的地方進行記錄。

8.1 在 console 輸出log

能夠將日誌信息輸出的console中,可是這種方式不經常使用。【GitHub示例】平常更多使用的是8.2的方法,將日誌信息輸出到log文件中。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : record_log.py @Time : 2019/8/28 19:15 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import logging


class RecordLog(object):
    def __init__(self):
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.DEBUG)

        # 1. 在 console 中輸出日誌文件
        # 可以將日誌信息輸出到sys.stdout, sys.stderr 或者類文件對象
        # 日誌信息會輸出到指定的stream中,若是stream爲空則默認輸出到sys.stderr。
        console = logging.StreamHandler(stream=None)
        # 將sys.stderr中的信息添加到logger中
        self.logger.addHandler(console)
        # 輸出調試信息
        self.logger.debug("這是一條在控制檯線上的log")
        # 關閉流
        console.close()
        # 移除
        self.logger.removeHandler(console)


if __name__ == "__main__":
    rl = RecordLog()
複製代碼

8.2 輸出log到文件

GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : record_log.py @Time : 2019/8/28 19:15 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import logging
import os
from datetime import datetime


class RecordLog(object):
    def __init__(self):
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.DEBUG)

        # 2.將log信息輸出到log文件中
        # 2.1 先定位看將log文件輸出到哪裏去
        current_dir = os.path.dirname(os.path.abspath(__file__))
        print(current_dir)  # D:\MySpace\Python\WebTesting\util
        log_dir = os.path.join('../logs')
        # 日誌名稱構建
        log_file_name = datetime.now().strftime("%Y-%m-%d") + '.log'
        log_file_path = log_dir + '/' + log_file_name
        print(log_file_path)

        # 2.2 好的,將日誌寫進log文件中
        self.file_handle = logging.FileHandler(log_file_path, 'a', encoding='utf-8')
        formatter = logging.Formatter(
            '%(asctime)s %(filename)s %(funcName)s %(levelno)s: [%(levelname)s] ---> %(message)s')
        self.file_handle.setFormatter(formatter)
        self.logger.addHandler(self.file_handle)

    def get_log(self):
        return self.logger

    def close_handle(self):
        self.logger.removeHandler(self.file_handle)
        self.file_handle.close()


if __name__ == "__main__":
    rl = RecordLog()
    log_info = rl.get_log()
    log_info.debug('輸出到文件中去')
    rl.close_handle()

複製代碼

9.識別驗證碼

  做爲一個通用模塊,咱們應該把識別驗證碼模塊封裝起來,能夠方便其餘部分的調用。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : discern_captcha.py @Time : 2019/8/29 23:22 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : 圖片驗證碼識別模塊 """

from api.ShowapiRequest import ShowapiRequest
from PIL import Image
from time import sleep
from selenium import webdriver


class DiscernCaptcha(object):
    def __init__(self, driver):
        self.driver = driver

    # 獲取照片
    def get_captcha_code_image(self, file_name):
        self.driver.save_screenshot(file_name)
        captcha_image = self.driver.find_element_by_id('getcode_num')
        left = captcha_image.location['x']
        top = captcha_image.location['y']
        right = captcha_image.size['width'] + left
        height = captcha_image.size['height'] + top
        im = Image.open(file_name)
        img = im.crop((left, top, right, height))
        img.save(file_name)
        sleep(3)

    # 識別圖片
    def discern_image(self, file_name):
        self.get_captcha_code_image(file_name)
        # 解析驗證碼圖片中的文字(用第三方的圖片驗證碼識別接口 ShowApiRequest)
        r = ShowapiRequest("http://route.showapi.com/184-4", "48120", "12c017278c0845c2bcda177212d2d2ac")
        r.addBodyPara("img_base64", "")
        r.addBodyPara("typeId", "35")
        r.addBodyPara("convert_to_jpg", "0")
        r.addBodyPara("needMorePrecise", "0")
        r.addFilePara("image", file_name)  # 文件上傳時設置
        res = r.post()
        text = res.json()["showapi_res_body"]["Result"]
        return text


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    driver = webdriver.Chrome('../tools/chromedriver.exe')
    driver.get(register_url)
    driver.maximize_window()
    dc = DiscernCaptcha(driver)
    file_name = '../image/discern_captcha/code_image.png'
    dc.get_captcha_code_image(file_name)
    dc.discern_image(file_name)
    driver.close()
複製代碼

3、PO模型

  PO 模型:將測試的每一個頁面看做一個對象,將這些對象抽象成類,完成頁面元素和業務操做;將測試類和 page 類區分開來,須要調用什麼類去取便可,下降耦合。當頁面元素髮生變化時,只需修改對應頁面類部分,其餘部分很可能作到最小修改。

  PO 模型的分層結構(以註冊頁面做爲page對象):

(1)register_page(頁面元素查找類) --->
(2)register_handle(操做層:將查找到的元素上傳遞數據) --->
(3)register_business(業務層:調用操做層,根據操做層傳遞的數據進行測試業務場景判斷,如驗證碼輸入錯誤場景等) --->
(4)register_cases(測試模塊:封裝業務層,進行測試用例業務組裝)。
複製代碼

1. 元素查找

  此頁面主要是查找註冊頁面中正常的元素和異常的元素(錯誤的提示信息)。【GitHub示例

register_email_error = id>register_email-error
register_nickname_error = id>register_nickname-error
register_password_error = id>register_password-error
captcha_code_error = id>captcha_code-error
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : regitser_page.py @Time : 2019/8/29 11:38 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from basic.find_element import FindElement
from selenium import webdriver

class RegisterPage(object):
    # 初始化元素查找類,執行該類的時候就會加載
    def __init__(self, driver):
        self.fe = FindElement(driver)

    # 註冊郵箱
    def get_register_email(self):
        return self.fe.get_element('register_email')

    # 用戶暱稱
    def get_register_nickname(self):
        return self.fe.get_element('register_nickname')

    # 密碼
    def get_register_password(self):
        return self.fe.get_element('register_password')

    # 驗證碼輸入框
    def get_getcode_num(self):
        return self.fe.get_element('getcode_num')

    # 驗證碼圖片
    def get_captcha_code(self):
        return self.fe.get_element('captcha_code')

    # 註冊郵箱框文本提示語
    def get_register_email_placeholder(self):
        print(self.fe.get_element('register_email').get_attribute('placeholder'))
        return self.fe.get_element('register_email').get_attribute('placeholder')

    # 用戶暱稱框文本提示語
    def get_register_nickname_placeholder(self):
        print(self.fe.get_element('register_nickname').get_attribute('placeholder'))
        return self.fe.get_element('register_nickname').get_attribute('placeholder')

    # 密碼框文本提示語
    def get_register_password_placeholder(self):
        print(self.fe.get_element('register_password').get_attribute('placeholder'))
        return self.fe.get_element('register_password').get_attribute('placeholder')

    # 驗證碼框文本提示語
    def get_captcha_code_placeholder(self):
        print(self.fe.get_element('captcha_code').get_attribute('placeholder'))
        return self.fe.get_element('captcha_code').get_attribute('placeholder')

   # 不合法註冊郵箱錯誤提示語
    def get_register_email_error(self):
        return self.fe.get_element('register_email_error')

    # 不合法註冊用戶錯誤提示語
    def get_register_nickname_error(self):
        return self.fe.get_element('register_nickname_error')

    # 不合法密碼錯誤提示語
    def get_register_password_error(self):
        return self.fe.get_element('register_password_error')

    # 不合法驗證碼錯誤提示語
    def get_captcha_code_error(self):
        return self.fe.get_element('captcha_code_error')


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    driver = webdriver.Chrome('../tools/chromedriver.exe')
    driver.get(register_url)
    rp = RegisterPage(driver)
    rp.get_register_email_placeholder()
    rp.get_register_nickname_placeholder()
    rp.get_register_password_placeholder()
    rp.get_captcha_code_placeholder()
    driver.close()
複製代碼

2. 操做層

  上一層咱們獲取到註冊頁面中主要元素信息,接下來就該給這些元素進行數據上的操做處理(賦值)。【(GitHub示例)[github.com/Crisimple/W…]

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_handle.py @Time : 2019/8/29 15:07 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : 給register_page中找到的元素賦值 """

from page_object_model.regitser_page import RegisterPage
from selenium import webdriver
from time import sleep

class RegisterHandle(object):
    def __init__(self, driver):
        self.rp = RegisterPage(driver)

    # 輸入註冊郵箱
    def send_register_email(self, email):
        self.rp.get_register_email().send_keys(email)

    # 輸入用戶暱稱
    def send_register_nickname(self, nickname):
        self.rp.get_register_nickname().send_keys(nickname)

    # 輸入註冊密碼
    def send_register_password(self, password):
        self.rp.get_register_password().send_keys(password)

    # 輸入驗證碼
    def send_register_captcha(self, captcha):
        self.rp.get_getcode_num().send_keys(captcha)

    # 獲取錯誤信息
    def get_user_text(self, error_info, error_value):
        text = None
        if error_info == "register_email_error":
            text = self.rp.get_register_email_error().send_keys(error_value)
        elif error_info == 'register_nickname_error':
            text = self.rp.get_register_nickname_error().send_keys(error_value)
        elif error_info == 'register_password_error':
            text = self.rp.register_password_error().send_keys(error_value)
        elif error_info == 'captcha_code_error':
            text = self.rp.captcha_code_error().send_keys(error_value)
        else:
            print("error element not found")
        return text

    # 點擊註冊按鈕
    def click_register_btn(self):
        self.rp.get_register_btn().send_keys()


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    driver = webdriver.Chrome('../tools/chromedriver.exe')
    driver.get(register_url)
    rh = RegisterHandle(driver)
    rh.send_register_email('jjij@163.com')
    rh.send_register_nickname('MiFan')
    rh.send_register_password('123@123abc')
    rh.send_register_captcha('qwer')
    rh.click_register_btn()
    sleep(5)
    driver.close()
複製代碼

3. 業務層

  業務層,也就是咱們要作些什麼,作事的邏輯是什麼?對於自動化測試來講,就是自動化的測試場景,也就是咱們的測試點邏輯。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_business.py @Time : 2019/8/29 17:35 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from page_object_model.register_handle import RegisterHandle
from selenium import webdriver
from time import sleep


class RegisterBusiness(object):
    def __init__(self, driver):
        self.rh = RegisterHandle(driver)

    # 正常註冊
    def common_register(self, register_email, nickname, password, captcha):
        self.rh.send_register_email(register_email)
        self.rh.send_register_nickname(nickname)
        self.rh.send_register_password(password)
        self.rh.send_register_captcha(captcha)

    # 判斷是否註冊成功
    def success_or_fail(self):
        if self.rh.get_register_btn_text() is None:
            return True
        else:
            return False

    # 郵箱錯誤
    def register_email_error(self, register_email, nickname, password, captcha):
        self.common_register(register_email, nickname, password, captcha)
        if self.rh.get_user_text('register_email_error', "請輸入有效的電子郵件地址") is None:
            print("註冊郵箱輸入錯誤")
            return True
        else:
            return False

    # 用戶暱稱錯誤
    def register_nickname_error(self, register_email, nickname, password, captcha):
        self.common_register(register_email, nickname, password, captcha)
        if self.rh.get_user_text('register_nickname_error', "字符長度必須大於等於4,一箇中文字算2個字符") is None:
            print("用戶暱稱錯誤")
            return True
        else:
            return False

    # 用戶密碼錯誤
    def register_password_error(self, register_email, nickname, password, captcha):
        self.common_register(register_email, nickname, password, captcha)
        if self.rh.get_user_text('register_password_error', "最少須要輸入 5 個字符") is None:
            print("用戶密碼錯誤")
            return True
        else:
            return False

    # 驗證碼錯誤
    def captcha_code_error(self, register_email, nickname, password, captcha):
        self.common_register(register_email, nickname, password, captcha)
        if self.rh.get_user_text('captcha_code_error', "驗證碼錯誤") is None:
            print("驗證碼錯誤")
            return True
        else:
            return False


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    driver = webdriver.Chrome('../tools/chromedriver.exe')
    driver.get(register_url)
    rb = RegisterBusiness(driver)
    rb.captcha_code_error('1243589@163.com', 'pass123', 'test@123', 'sds')

    sleep(3)
    driver.close()
複製代碼

4. 測試層

  通過一些列分層計劃,雖然在每一層咱們都在進行測試,可是咱們最終仍是要回歸到本質進行測試,將各層模塊一同調用起來。基於業務層編寫測試用例。【GitHub示例

  (這裏留了個問題,就是兩條測試數據之間頁面沒有刷新從新輸入,致使數據在一個頁面重複輸入)【

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_testcases.py @Time : 2019/8/29 21:20 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from page_object_model.register_business import RegisterBusiness
from selenium import webdriver
import unittest


class RegisterTestcase(unittest.TestCase):
 @classmethod
    def setUpClass(cls) -> None:
        cls.register_url = 'http://www.5itest.cn/register'
        cls.driver = webdriver.Chrome('../tools/chromedriver.exe')
        cls.driver.get(cls.register_url)
        cls.driver.maximize_window()
        cls.rb = RegisterBusiness(cls.driver)

 @classmethod
    def tearDownClass(cls) -> None:
        cls.driver.close()

    # 註冊郵箱錯誤,但用例執行成功
    def test_register_email_error(self):
        register_email_error = self.rb.register_email_error('23', 'test01', 'test01abc', 'abc4')
        if register_email_error is True:
            print("帳號註冊失敗,該用例執行成功")
        else:
            print("帳號註冊成功,該用例執行失敗")

    # 驗證碼錯誤,但用例執行成功‘
    def test_captcha_code_error(self):
        captcha_code_error = self.rb.captcha_code_error('test02@163.com', 'test02', 'test02abc', 'height')
        if captcha_code_error is True:
            print("帳號註冊失敗,該用例執行成功")
        else:
            print("帳號註冊成功,該用例執行失敗")


if __name__ == "__main__":
    unittest.main()
複製代碼

4、數據驅動框架

  那麼問題來了,什麼是數據驅動呢?就是,數據的改變從而驅動自動化測試的執行,最終引發測試結果的改變,也就是參數的應用化。

  這裏對於數據驅動測試,《蟲師》的兩篇博文寫的很到位【《使用「數據驅動測試」以前應該知道的》《使用「數據驅動測試」以前你應該知道的》】。總結起來就是,數據驅動絕非讀取文件(excel、csv、xml)中數據進行參數的賦值測試,由於採用的這種方式的測試,工做重心反而變成了如何讀寫文件,而對於自動化測試中關心的執行結果統計、斷言結果反而不是那麼容易去實現。尤爲是測試頁面結構發生大的調整時,文件類的字段調整獲取也要發生較大的修改,因此文件數據驅動測試也是能夠的,可是並非最優解。

  那麼什麼纔是最優的數據驅動測試呢?是的,用單元測試 unittest 結合 ddt 庫。使用單元測試能夠很方便的解決兩個問題:

(1)斷言。利用單元測試的斷言機制,咱們能夠方便的進行預期結果和實際結果的對比;
(2)數據統計。執行完測試用例後,一共執行了多少條用例,執行成功多少,失敗多少,失敗的用例錯誤在哪裏?單元測試框架會幫咱們統計展現。
複製代碼

1. DDT 入門

  Python 的 unittest 沒有自帶數據驅動功能,若是使用 unittest, 同時又想使用數據驅動,就用 DDT 吧。奉上 ddt 官方文檔 《DDT 官方文檔》。

DDT 的使用方法:

(1) ddt.ddt --- 裝飾類,也就是繼承自 TestCase 的類;
(2) ddt.data --- 裝飾測試方法,參數是一系列的值。
(3) ddt.file_data --- 裝飾測試方法,參數是文件名。文件能夠是 json 或 yaml 類型,除.yaml結尾的文件,其餘文件均會做爲json文件處理。
(4) ddt.unpack --- 傳遞的是複雜的數據結構是使用。如:元組或列表。添加到 unpack 上以後,ddt 會自動把元組或者列表對應到多個參數上。
(5) 測試用例方法名生成規則 --- 
複製代碼

示例用法【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : ddt_introduction.py @Time : 2019/8/30 10:35 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import unittest
import ddt

@ddt.ddt
class DDTExample(unittest.TestCase):
 @classmethod
    def setUpClass(cls) -> None:
        print(cls.__name__)

 @classmethod
    def tearDownClass(cls) -> None:
        print('...end...')

 @ddt.data(
        [1, 2],
        [3, 4],
        [5, 6]
    )
 @ddt.unpack
    def test_add(self, a, b):
        print(a + b)


if __name__ == "__main__":
    unittest.main()
複製代碼

2. 實踐應用

  將 ddt 引入到自動化測試中。【GitHub示例

  可是這裏一樣埋着一個BUG,稍後解決。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_ddt_cases.py @Time : 2019/8/30 14:19 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

import ddt
from page_object_model.register_business import RegisterBusiness
from selenium import webdriver
import unittest
from time import sleep


@ddt.ddt
class RegisterDdtCases(unittest.TestCase):
 @classmethod
    def setUpClass(cls) -> None:
        cls.register_url = 'http://www.5itest.cn/register'
        cls.driver = webdriver.Chrome('../tools/chromedriver.exe')
        cls.driver.maximize_window()
        cls.driver.get(cls.register_url)
        sleep(3)
        cls.rb = RegisterBusiness(cls.driver)

 @classmethod
    def tearDownClass(cls) -> None:
        sleep(2)
        cls.driver.close()

    # 郵箱錯誤測試的測試用例
 @ddt.data(
        # 順序分別是:註冊郵箱、用戶暱稱、註冊密碼、驗證碼、錯誤信息定位元素、錯誤提示信息
        ['123', 'test01', 'test01abc', 'tyu9'],
        ['@163.com', 'test01', 'test01abc', 'tyu9'],
        ['@163', 'test01', 'test01abc', 'tyu9']
    )
 @ddt.unpack
    def test_ddt_email_error(self, register_email, nickname, password, captcha):
        register_email_error = self.rb.register_email_error(register_email, nickname, password, captcha)
        print("register_email_error: ", register_email_error)
        self.assertFalse(register_email_error, '你輸入的郵箱錯誤,但此條測試用例執行成功')


if __name__ == "__main__":
    unittest.main()
複製代碼

5、關鍵字模型

  關鍵字簡單來講就是,把咱們的執行操做每個關鍵步驟當成一個關鍵字來對待,用來驅動程序的設計開發。例如:進行web自動化咱們的首要是打開瀏覽器,是的 「打開瀏覽器」 咱們就能夠做爲一個關鍵字來對待它,關鍵字就是來驅動咱們程序設計的關鍵步驟。經過關鍵字的改變從而驅動自動化測試的執行,最終引發測試結果的改變。

  對於測試一個註冊頁面,咱們來梳理下看有哪些關鍵詞,更深層次瞭解下關鍵詞模型:

(1) 打開瀏覽器 --->  打開瀏覽器
(2) 輸入註冊頁面的url --->  輸入測試地址
(3) 頁面加載等待 ---> 頁面加載等待
(4) 輸入(註冊郵箱、用戶名、密碼、驗證碼)--->  輸入元素
(5) 點擊註冊按鈕 ---> 點擊元素
(6) 退出瀏覽器 
複製代碼

1. 構建關鍵詞類

GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_keyword.py @Time : 2019/8/30 23:59 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """
from selenium import webdriver
from basic.find_element import FindElement
from time import sleep


class RegisterKeyword(object):
    def __init__(self, driver):
        self.fe = FindElement(driver)

    # 打開瀏覽器
    def open_browser(self, browser):
        if browser == 'chrome':
            self.driver = webdriver.Chrome('../tools/chromedriver.exe')
        elif browser == 'firefox':
            self.driver = webdriver.Firefox()
        else:
            self.driver = webdriver.Edge()

    # 輸入測試地址
    def get_url(self, url):
        self.driver.get(url)

    # 定位元素
    def get_element(self, key):
        return self.fe.get_element(key)

    # 輸入元素
    def send_element_key(self, key, value):
        get_element = self.get_element(key)
        get_element.send_keys(value)

    # 點擊元素
    def click_element(self, key):
        self.fe.get_element(key).click()

    # 頁面等待
 @staticmethod
    def wait_loading():
        sleep(3)

    # 關閉瀏覽器
    def close_browser(self):
        self.driver.close()


if __name__ == "__main__":
    register_url = 'http://www.5itest.cn/register'
    driver = webdriver.Chrome('../tools/chromedriver.exe')
    driver.get(register_url)
    rk = RegisterKeyword(driver)
    print(rk.get_element('register_email'))
    driver.close()
複製代碼

2. 關鍵詞模型測試用例

  咱們關鍵詞方法直接從 register_keyword 中讀取便可,可是測試數據從哪獲取到從而傳給相應的關鍵詞方法呢?爲了方便測試數據方便管理,咱們能夠將其存儲到 Excel中去並獲取。

2.1 讀取Excel

(GitHub示例)[github.com/Crisimple/W…]

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : read_excel.py @Time : 2019/9/1 0:25 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : 讀寫 excel 文件 """
import xlrd
from xlutils.copy import copy


class ReadExcel(object):
    def __init__(self, excel_path=None, index=None):
        if excel_path is None:
            self.excel_path = '../data/register_keyword_testdata.xls'
            self.index = 0
        else:
            self.excel_path = excel_path
            self.index = index

        # 打開 excel 文件,獲取數據列表
        self.data = xlrd.open_workbook(self.excel_path)
        # 讀取第一 sheet 頁的數據
        self.table = self.data.sheets()[0]

    def get_data(self):
        result = []
        rows = self.get_lines()
        if rows != '':
            for i in range(rows):
                col = self.table.row_values(i)
                result.append(col)
            return result
        return None

    # 獲取 excel 行數
    def get_lines(self):
        rows = self.table.nrows
        if rows >= 1:
            return rows
        return None

    # 獲取單元格的值
    def get_cell(self, row, col):
        if self.get_lines() > row:
            data = self.table.cell(row, col).value
            return data
        return None

    def write_data(self, row, col, value):
        read_data = xlrd.open_workbook(self.excel_path)
        write_data = copy(read_data)
        write_data.get_sheet(self.index).write(row, col, value)
        write_data.save("../data/register_keyword_testdata.xls")
        write_data.save(self.excel_path)


if __name__ == "__main__":
    re = ReadExcel()
    print(re.get_data())
    print(re.get_lines())
    print(re.get_cell(0, 0))
    re.write_data(11, 0, 123456)
複製代碼

2.2 測試用例實踐

  前面將測試測數據存在到excel中了,接下來怎麼寫關鍵字對應的測試用例。【GitHub示例

(1) 拿到操做值,是否執行
(2) 拿到執行方法
(3) 拿到輸入數據
(4) 是否有輸入數據
        執行方法(輸入數據,操做元素)
    沒有輸入數據
        執行方法(操做元素)
     
(5) 對比預期結果和實際結果的值
    對比結果同樣,測試結論爲pass;不然爲fail
複製代碼
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" @File : register_keyword_cases.py @Time : 2019/9/1 0:03 @Author : Crisimple @Github : https://crisimple.github.io/ @Contact : Crisimple@foxmail.com @License : (C)Copyright 2017-2019, Micro-Circle @Desc : None """

from util.read_excel import ReadExcel
from keyword_model.register_keyword import RegisterKeyword
from selenium import webdriver


class RegisterKeywordCases(object):
    def __init__(self):
        self.rk = RegisterKeyword()
        self.excel_path = '../data/register_keyword_testdata.xls'

    # 執行關鍵字測試方法
    def run_keyword_method(self, keyword_method, operator_element='', send_value=''):
        print('keyword_method ---> ', keyword_method)
        print("operator_element ---> ", operator_element)
        print("send_value ---> ", send_value)
        execute_method = getattr(self.rk, keyword_method)
        print(execute_method)
        if operator_element is '' and send_value is not '':
            result = execute_method(send_value)
        elif operator_element is not '' and send_value is '':
            result = execute_method(operator_element)
        elif operator_element is '' and send_value is '':
            result = execute_method()
        else:
            result = execute_method(operator_element, send_value)
        return result

    # 執行關鍵詞測試用例
    def run_keyword_excel_cases(self):
        handle_excel = ReadExcel(self.excel_path)

        # 獲取 excel 關鍵詞測試用例的條數
        cases_numbers = handle_excel.get_lines()
        print("註冊頁獲取到的關鍵詞測試的測試用例條數爲:%s" % cases_numbers)

        # 循環遍歷測試用例
        if cases_numbers:
            # 第 0 行是標題行不做爲用例執行
            for i in range(1, cases_numbers):
                # 獲取測試用例的名稱
                testcase_name = handle_excel.get_cell(i, 0)
                # 獲取用例是否執行
                is_run = handle_excel.get_cell(i, 1)
                if is_run == 'yes':
                    keyword_method = handle_excel.get_cell(i, 2)
                    operator_element = handle_excel.get_cell(i, 3)
                    send_value = handle_excel.get_cell(i, 4)
                    except_result = handle_excel.get_cell(i, 5)
                    actual_result = handle_excel.get_cell(i, 6)

                    # 反射
                    self.run_keyword_method(keyword_method, operator_element, send_value)

                    # if except_result is not '':
                    # except_value = self.run_keyword_method(keyword_method)
                else:
                    print('第 %s 條用例不執行,用例名稱是: [%s],無預期結果' % (i, testcase_name))
        else:
            print("略略略~,請檢查你是否有寫測試用例!")


if __name__ == "__main__":
    rkc = RegisterKeywordCases()
    # rkc.run_keyword_method('open_browser', '', 'chrome')
    # rkc.run_keyword_method('get_url', '', 'http://www.5itest.cn/register')
    rkc.run_keyword_excel_cases()

複製代碼

6、行爲驅動模型

  什麼是行爲驅動測試呢?行爲驅動(Behave Driven Development)測試,是一種敏捷的開發方法,一般應用在自動化測試中,經過使用天然描述語言肯定自動化腳本。

GitHub示例

不懂不懂,什麼亂七八糟的,不喜歡這種方式用在自動化腳本里。

相關文章
相關標籤/搜索