自動化測試之數(shù)據(jù)驅(qū)動與關(guān)鍵字驅(qū)動

一、概述

數(shù)據(jù)驅(qū)動的自動化測試

從數(shù)據(jù)文件讀取輸入數(shù)據(jù),通過變量的參數(shù)化,將測試數(shù)據(jù)傳入測試腳本,不同的數(shù)據(jù)文件對應不同的測試用例。在這種模式下數(shù)據(jù)和腳本分離,腳本的利用率、可維護性大大提高,數(shù)據(jù)的覆蓋率也較高,但受界面變化的影響仍然很大。

關(guān)鍵字驅(qū)動的自動化測試

關(guān)鍵字驅(qū)動測試是數(shù)據(jù)驅(qū)動測試的一種改進類型,它將測試邏輯按照關(guān)鍵字進行分解,形成數(shù)據(jù)文件,關(guān)鍵字對應封裝的業(yè)務邏輯。

主要關(guān)鍵字包括三類:被操作對象(Item)、操作(Operation)和值(value),依據(jù)不同對象還有其他對應參數(shù)。

關(guān)鍵字驅(qū)動的主要思想是:腳本與數(shù)據(jù)分離、界面元素名與測試內(nèi)部對象名分離、測試描述與具體實現(xiàn)細節(jié)分離。數(shù)據(jù)驅(qū)動的自動化測試框架在受界面影響方面,較數(shù)據(jù)驅(qū)動和錄制/回放有明顯的優(yōu)勢,可根據(jù)界面的變化更新對應的關(guān)鍵字對象,而不用重新錄制腳本。

二、 數(shù)據(jù)驅(qū)動模式—DDT

1、核心原理

程序不變,數(shù)據(jù)變

即:多個測試用例的執(zhí)行過程和操作一樣的,只不過測試使用的數(shù)據(jù)和驗證結(jié)果有所不同,數(shù)據(jù)驅(qū)動就是把測試數(shù)據(jù)與測試腳本進行分離,把數(shù)據(jù)放到配置文件中

2、適用場景

測試過程比較簡單,但是需要使用大量的測試數(shù)據(jù)進行輸入驗證,適合個人測試

3、ddt安裝

ddt是python的第三方庫,安裝用命令:pip install ddt 即可

4、ddt模塊

ddt模塊包含類的裝飾器ddt和兩個方法裝飾器data

  • ddt.ddt:裝飾類,也就是繼承TestCase的類
  • ddt.data:裝飾測試方法,參數(shù)是一系列的值
  • ddt.file_data:裝飾測試方法,參數(shù)是文件名。文件可以是json或者yaml類型
    -- ① 如果文件是以“.yml”或者".yaml"結(jié)尾,ddt會作為yaml類型處理,其他文件都會作為json文件處理
    -- ② 如果文件是列表,列表的值會作為測試用例參數(shù),同時,會作為測試用例方法名后綴顯示
    -- ③ 如果文件是字典,字典的key會作為測試用例方法的后綴顯示,字典的value會作為測試用例參數(shù)
  • ddt.unpack:傳遞的是復雜的數(shù)據(jù)結(jié)構(gòu)時使用,比如使用列表或者元組,添加unpack后,ddt會自動把元組或者列表對應到多個參數(shù)上

5、案例演示

方式1:參數(shù)直接放在執(zhí)行腳本文件里

# 文件名:Internal_parameters_ddt.py
import unittest
import ddt
from selenium import webdriver
import time

from selenium.webdriver.common.by import By


@ddt.ddt
class Praddt(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()

    def tearDown(self):
        self.driver.quit()

    @ddt.data(
        ["數(shù)據(jù)驅(qū)動測試", "DDT"],
        ["關(guān)鍵字驅(qū)動測試", "51"],
        ["混合驅(qū)動測試", "python"]
    )
    @ddt.unpack
    def test_ddt(self, search_word, expect_word):
        self.driver.get('http://www.baidu.com')
        self.driver.find_element(By.ID,"kw").send_keys(search_word)
        self.driver.find_element(By.ID,"su").click()
        time.sleep(2)
        assert expect_word in self.driver.page_source

if __name__ == '__main__':
    unittest.main()

方式2:參數(shù)放在執(zhí)行腳本外部數(shù)據(jù)文件里
外部數(shù)據(jù)文件data.txt里面的數(shù)據(jù)如下:

數(shù)據(jù)驅(qū)動測試;;;DDT
關(guān)鍵字驅(qū)動測試;;;51
混合驅(qū)動測試;;;python

執(zhí)行腳本文件代碼如下:

# 文件名:External_parameters_ddt.py
from selenium import webdriver
import time
import sys

# 讀取data.txt文件
from selenium.webdriver.common.by import By


def get_test_datas(file_path):
    with open(file_path,encoding='utf-8') as fp:
        test_datas = fp.readlines()
    return test_datas

# 獲取數(shù)據(jù)文件中的search_word和expect_word
test_datas = get_test_datas('data.txt')

if len(test_datas) == 0:
    print('測試數(shù)據(jù)文件數(shù)據(jù)為空,請檢查后再測試')
    sys.exit()

# 每獲取一組數(shù)據(jù)后,都執(zhí)行下面的測試步驟
for i in range(len(test_datas)):
    search_word,expect_word = test_datas[i].strip().split(';;;') # 去掉列表數(shù)據(jù)中的“\n”、“;;;”
    try:
        driver = webdriver.Chrome()
        driver.get('http://www.baidu.com')
        driver.find_element(By.ID,"kw").send_keys(search_word)
        driver.find_element(By.ID,"su").click()
        time.sleep(2)
        assert expect_word in driver.page_source
        driver.quit()
    except AssertionError:
        print('沒有找到斷言內(nèi)容{}'.format(expect_word))
        driver.quit()
    except Exception as e:
        print('出現(xiàn)未知錯誤')
        driver.quit()

二、關(guān)鍵字驅(qū)動模式—KDT

1、核心原理

把函數(shù)名稱和程序進行分離
關(guān)鍵字是指測試步驟中的某個動作,將其封裝成函數(shù)的名稱,即:把函數(shù)名放在配置文件中,然后從配置文件中讀出函數(shù)名稱以及函數(shù)對應的參數(shù),組合成函數(shù)調(diào)用表達式來進行函數(shù)的調(diào)用

測試步驟由:關(guān)鍵字、操作對象的定位表達式、操作值 三個部分組成。將這三個部分通過字符串拼接的方式,拼成一個函數(shù)的調(diào)用,以此來執(zhí)行測試步驟的執(zhí)行
例如:
測試步驟:在百度輸入框輸入“數(shù)據(jù)驅(qū)動測試”

拼接調(diào)用:input||kw||數(shù)據(jù)驅(qū)動測試===>input(‘kw’,‘數(shù)據(jù)驅(qū)動測試’)

2、適用場景

可以在應用未提交測試之前,就可以建立關(guān)鍵字驅(qū)動測試用例對象庫,適合大團隊大項目里面實施,可以讓不懂代碼的測試人員也能做自動化測試。RobotFramework自動化測試框架采用的模式就是關(guān)鍵字驅(qū)動,常用于UI自動化測試。

3、案例演示

需求場景1:用谷歌瀏覽器打開百度,輸入一個詞進行搜索,再對搜索結(jié)果進行校驗
解決方案:將以上步驟用代碼一一實現(xiàn)(面向過程)

# 文件名:kdt_v1.py
from selenium import webdriver
import time

from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
search_box = driver.find_element(By.ID,"kw")
search_box.send_keys('數(shù)據(jù)驅(qū)動測試')
submit_button = driver.find_element(By.ID,"kw")
submit_button.click()
time.sleep(2)
assert 'DDT' in driver.page_source
driver.quit()

需求場景2:用谷歌瀏覽器打開百度,輸入不同的詞進行搜索,再對搜索結(jié)果進行一一校驗,如果將以上操作步驟代碼又重復寫一遍的話,就會顯得很繁瑣了
解決方案:將重復的操作步驟代碼進行封裝,直接通過txt文檔寫測試用例(面向?qū)ο螅?/p>

# 文件名:kdt_v2.py
from selenium import webdriver
import time

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


class KeyWord:

    _solit_chr = ";;;"

    def open_browser(self,browser_name):

        if 'chrome' in browser_name.lower():
            self.driver = webdriver.Chrome()
        elif 'ie' in browser_name.lower():
            self.driver = webdriver.Ie()
        else:
            self.driver = webdriver.Firefox()
        self.wait = WebDriverWait(self.driver, 10)

    def find_element(self, loc: str):
        value, *by = loc.split(self._solit_chr)
        if not by:
            by = By.XPATH
        else:
            by = getattr(By, by[0])
        ele = self.wait.until(lambda _: self.driver.find_element(by, value))
        return ele

    def visit(self,url):
        self.driver.get(url)

    def input(self,element,value):
        self.find_element(element).send_keys(value)

    def click(self,element):
        self.find_element(element).click()

    def sleep(self,seconds):
        time.sleep(int(seconds))

    def assertion(self,expect_word):
        assert expect_word in self.driver.page_source

    def quit(self):
        self.driver.quit()

# 調(diào)試
if __name__ == '__main__':
    kdt = KeyWord()
    kdt.open_browser('chrome')
    kdt.visit('http://www.baidu.com')
    kdt.input('kw;;;ID','數(shù)據(jù)驅(qū)動測試')
    kdt.click('su;;;ID')
    kdt.sleep(2)
    kdt.assertion('DDT')
    quit()

修改后的代碼看起來貌似是比前面的代碼多了很多行,但是這里的核心思想是代碼可以復用,具體怎么復用呢?此時我們需要新建一個測試用例文件test_steps.txt,里面內(nèi)容如下:

open_browser||chrome
visit||https://www.baidu.com/
input||kw;;;ID||數(shù)據(jù)驅(qū)動測試
click||su;;;ID
sleep||2
assertion||DDT
quit

然后在封裝的代碼里面添加讀取測試用例文件的函數(shù),實現(xiàn)關(guān)鍵字驅(qū)動測試

關(guān)鍵字驅(qū)動的核心是:將自然語言(如: open_browser||chrome) 轉(zhuǎn)換成 (如:open_browser(“chrome”) )函數(shù)的調(diào)用

具體函數(shù)的實現(xiàn)思路拆解:

  • ① 定義test_steps.txt,所有的測試步驟(關(guān)鍵字實現(xiàn)的)
  • ② 框架程序要讀test_steps.txt,所有的行放到一個列表中,列表中的每一個元素是文件中的一行
    test_steps = [“open_browser||chrome\n”,“visit||https://www.baidu.com/\n”,…]
  • ③ 判斷test_steps.txt列表中一共有多少元素,就知道有多少行,也就有多少個測試步驟
  • ④ 使用for循環(huán),有多少個測試步驟,就循環(huán)多少次
  • ⑤ 使用strip去掉換行符\n,使用split做切割分別得到關(guān)鍵字和對應參數(shù):

【情況1】:
有兩個“||”,比如:“input||kw;;;ID||數(shù)據(jù)驅(qū)動測試\n”
step = “input||kw||數(shù)據(jù)驅(qū)動測試\n”.strip().split("||")
得到結(jié)果:[‘input’, ‘kw;;;ID’, ‘數(shù)據(jù)驅(qū)動測試’]
keyword = step[0]
element = step[1]
value = setp[2]

【情況2】:
有一個“||”,比如:“open_browser||chrome\n”
step = “open_browser||chrome\n”.strip().split("||")
得到結(jié)果:[‘open_browser’, ‘chrome’]
keyword = step[0]
element = None
value = setp[1]

【情況3】:
沒有“||”,比如:“quit”
keyword = step[0]

  • ⑥ 取到的幾個值,最終要拼成一個函數(shù)調(diào)用的字符串:
    command = ‘open_browser(“chrome”)’
    command = ‘input(“kw;;;ID”,“數(shù)據(jù)驅(qū)動測試”)’
    command = ‘quit()’
  • ⑦ 用Python中的eval函數(shù)來調(diào)用字符串表達式
    eval(command)

完整代碼如下:

# 文件名:kdt_v3.py
import sys

from selenium import webdriver
import time

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


class KeyWord:

    _solit_chr = ";;;"

    def open_browser(self,browser_name):

        if 'chrome' in browser_name.lower():
            self.driver = webdriver.Chrome()
        elif 'ie' in browser_name.lower():
            self.driver = webdriver.Ie()
        else:
            self.driver = webdriver.Firefox()
        self.wait = WebDriverWait(self.driver, 10)

    def find_element(self, loc: str):
        value, *by = loc.split(self._solit_chr)
        if not by:
            by = By.XPATH
        else:
            by = getattr(By, by[0])
        ele = self.wait.until(lambda _: self.driver.find_element(by, value))
        return ele

    def visit(self,url):
        self.driver.get(url)

    def input(self,element,value):
        self.find_element(element).send_keys(value)

    def click(self,element):
        self.find_element(element).click()

    def sleep(self,seconds):
        time.sleep(int(seconds))

    def assertion(self,expect_word):
        assert expect_word in self.driver.page_source

    def quit(self):
        self.driver.quit()

def get_test_steps_data(file_path):
    # 讀取文件數(shù)據(jù)
    with open(file_path,encoding="utf-8-sig") as fp:
        test_datas = fp.readlines()
    return test_datas

test_steps = get_test_steps_data('test_steps.txt')

if len(test_steps) == 0:
    print('測試步驟文件的數(shù)據(jù)為空,請檢查后再測試')
    sys.exit()

kdt = KeyWord()
# 讀取的文件拼接成命令代碼
for i in test_steps:
    if i.count('||') == 2:
        keyword,element,value = i.strip().split('||')
        command = 'kdt.{0}("{1}","{2}")'.format(keyword,element,value)
    elif i.count('||') == 1:
        keyword,value = i.strip().split('||')
        command = 'kdt.{0}("{1}")'.format(keyword,value)
    elif i.count("||") == 0:
        keyword = i.strip()
        command = 'kdt.{}()'.format(keyword)

    try:
        # 通過eval()函數(shù)執(zhí)行command的代碼
        eval(command)
    except:
        flag = False
        print('{} 測試用例執(zhí)行失敗'.format(command))
    else:
        print('{} 測試用例執(zhí)行成功'.format(command))

三、混合模式驅(qū)動模式—HDT

1、核心原理

數(shù)據(jù)驅(qū)動+關(guān)鍵字驅(qū)動=混合驅(qū)動
就是數(shù)據(jù)驅(qū)動和關(guān)鍵字驅(qū)動的聯(lián)合,既用到數(shù)據(jù)驅(qū)動模式也用到關(guān)鍵字驅(qū)動模式

2、適用場景

需要實現(xiàn):關(guān)鍵字(函數(shù)名)和程序分離、數(shù)據(jù)(包括函數(shù)需要的參數(shù))和程序分離的場景
測試人員只需在配置文件中維護好關(guān)鍵字和數(shù)據(jù)信息即可

3、案例演示

需求場景:用谷歌瀏覽器打開百度,輸入不同的詞進行搜索,再對搜索結(jié)果進行一一校驗(測試數(shù)據(jù)和測試步驟都放在配置文件中)

測試數(shù)據(jù)文件:test_datas.txt

{"search_word":"數(shù)據(jù)驅(qū)動測試", "expect_word":"DDT"}
{"search_word":"關(guān)鍵字驅(qū)動測試", "expect_word":"51"}
{"search_word":"混合驅(qū)動測試", "expect_word":"python"}

測試步驟文件:test_steps.txt

open_browser||chrome
visit||https://www.baidu.com/
input||kw;;;ID||{{search_word}}
click||su;;;ID
sleep||2
assertion||{{expect_word}}
quit

完整代碼如下:

# 文件名:hdt.py
import os
import re
import sys

from selenium import webdriver
import time

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


class KeyWord:

    _solit_chr = ";;;"

    def open_browser(self,browser_name):

        if 'chrome' in browser_name.lower():
            self.driver = webdriver.Chrome()
        elif 'ie' in browser_name.lower():
            self.driver = webdriver.Ie()
        else:
            self.driver = webdriver.Firefox()
        self.wait = WebDriverWait(self.driver, 10)

    def find_element(self, loc: str):
        value, *by = loc.split(self._solit_chr)
        if not by:
            by = By.XPATH
        else:
            by = getattr(By, by[0])
        ele = self.wait.until(lambda _: self.driver.find_element(by, value))
        return ele

    def visit(self,url):
        self.driver.get(url)

    def input(self,element,value):
        self.find_element(element).send_keys(value)

    def click(self,element):
        self.find_element(element).click()

    def sleep(self,seconds):
        time.sleep(int(seconds))

    def assertion(self,expect_word):
        assert expect_word in self.driver.page_source

    def quit(self):
        self.driver.quit()


# 讀取測試數(shù)據(jù)
def get_test_datas(test_data_file_path):
    if not os.path.exists(test_data_file_path):
        print('{}測試數(shù)據(jù)文件不存在,請確認!'.format(test_data_file_path))
        sys.exit(0)
    test_datas = []
    with open(test_data_file_path, encoding='utf-8-sig') as fp:
        for line in fp:
            test_datas.append(line.strip())
    return test_datas


# 讀取測試步驟
def get_test_steps(test_steps_file_path):
    if not os.path.exists(test_steps_file_path):
        print('{}測試步驟文件不存在,請確認!'.format(test_steps_file_path))
        sys.exit(0)
    test_steps = []
    with open(test_steps_file_path, encoding='utf-8-sig') as fp:
        for line in fp:
            test_steps.append(line.strip())
    return test_steps


test_datas = get_test_datas('test_datas.txt')

kdt = KeyWord()

# 有幾行測試數(shù)據(jù),就執(zhí)行幾次測試步驟
for test_data in test_datas:
    '''{"search_word":"數(shù)據(jù)驅(qū)動測試", "expect_word":"ddt"} 是json串,把json串轉(zhuǎn)換為字典類型'''
    test_data = eval(test_data)
    test_steps = get_test_steps('test_steps.txt')
    for test_step in test_steps:
        '''
        1)把test_step中{{xxx}}里面的xxx找出來,用正則:re.search(r"{{(.*?)}}")
        2)用test_data['xxx']把{{xxx}}替換掉
        '''
        if '{{' in test_step:
            key = re.search(r"{{(.*?)}}", test_step).group(1)
            test_step = re.sub(r"{{%s}}" % key, test_data[key], test_step)
        print(test_step)

        # 讀取的測試步驟拼接成命令代碼
        if test_step.count('||') == 2:
            keyword, element, value = test_step.strip().split('||')
            command = 'kdt.{0}("{1}","{2}")'.format(keyword, element, value)
        elif test_step.count('||') == 1:
            keyword, value = test_step.strip().split('||')
            command = 'kdt.{0}("{1}")'.format(keyword, value)
        elif test_step.count("||") == 0:
            keyword = test_step.strip()
            command = 'kdt.{}()'.format(keyword)

        try:
            '''通過eval()函數(shù)執(zhí)行command的代碼'''
            eval(command)
        except:
            flag = False
            print('{} 測試用例執(zhí)行失敗'.format(command))
        else:
            print('{} 測試用例執(zhí)行成功'.format(command))

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容