国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

從零搭建完整python自動(dòng)化測(cè)試框架(UI自動(dòng)化和接口自動(dòng)化

這篇具有很好參考價(jià)值的文章主要介紹了從零搭建完整python自動(dòng)化測(cè)試框架(UI自動(dòng)化和接口自動(dòng)化。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

從零搭建完整python自動(dòng)化測(cè)試框架(UI自動(dòng)化和接口自動(dòng)化)

文章目錄

總體框架

PO模式、DDT數(shù)據(jù)驅(qū)動(dòng)、關(guān)鍵字驅(qū)動(dòng)

框架技術(shù)選擇

框架運(yùn)行結(jié)果

各用例對(duì)應(yīng)的定義方式(PO/DDT)

測(cè)試執(zhí)行結(jié)果

從零開始搭建項(xiàng)目

一、開發(fā)環(huán)境搭建

二、新建項(xiàng)目

三、基礎(chǔ)功能實(shí)現(xiàn)

1. 配置功能實(shí)現(xiàn)(Conf)

2. 日志功能實(shí)現(xiàn)(Log)

3. 讀取EXCEL實(shí)現(xiàn)(data)

4. 郵件發(fā)送實(shí)現(xiàn)(Email)

四、WEB UI自動(dòng)化

1. 頁面PO對(duì)象配置

2. 實(shí)現(xiàn)basePage基類

3. 寫業(yè)務(wù)測(cè)試用例

五、實(shí)現(xiàn)主程序

1. API對(duì)象配置

2.實(shí)現(xiàn)base_api基類

3.測(cè)試用例


本自動(dòng)化測(cè)試框架采用python + unittest 的基礎(chǔ)來搭建,采用PO模式、數(shù)據(jù)驅(qū)動(dòng)的思想,通過selenium來實(shí)現(xiàn)WEB UI自動(dòng)化,通過request來實(shí)現(xiàn)接口自動(dòng)化。移動(dòng)終端的自動(dòng)化也可在該框架基礎(chǔ)上去構(gòu)建補(bǔ)充。

總體框架

總體框架如下圖:

python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試

用例掃描、測(cè)試結(jié)果反饋,如要和其它項(xiàng)目管理系統(tǒng)或是用例管理系統(tǒng)對(duì)接(比如testlink),就需要單獨(dú)出來進(jìn)行處理。
對(duì)于大型的產(chǎn)品,用例數(shù)特別多的話,需要建設(shè)一個(gè)master節(jié)點(diǎn),專門負(fù)責(zé)管理用例和腳本,分發(fā)測(cè)試腳本,指定測(cè)試環(huán)境,匯總測(cè)試結(jié)果等。各節(jié)點(diǎn)執(zhí)行分給自己的測(cè)試用例即可。

PO模式、DDT數(shù)據(jù)驅(qū)動(dòng)、關(guān)鍵字驅(qū)動(dòng)

PO模式(Page Object)是UI自動(dòng)化測(cè)試常采用的一種設(shè)計(jì)模式,用于解決開發(fā)頻繁修改UI頁面而導(dǎo)致的自動(dòng)化腳本維護(hù)困難的問題。
PO模式中心思想:

  1. 每一個(gè)頁面為一個(gè)對(duì)象;
  2. 每一個(gè)對(duì)象維護(hù)著頁面中的各元素和操作方法;
  3. 用例測(cè)試腳本只需要聚集業(yè)務(wù)邏輯和測(cè)試數(shù)據(jù);
  4. UI頁面的變更,只需要修改對(duì)應(yīng)的PO對(duì)象,無需修改測(cè)試腳本(理想情況下。實(shí)際上也很難100%做到,因?yàn)閁I的變更很多時(shí)候意味著業(yè)務(wù)邏輯的變更)。

DDT(Data Driven Testing)數(shù)據(jù)驅(qū)動(dòng)測(cè)試模式,用來解決部分自動(dòng)化用例邏輯完全相同,只有測(cè)試數(shù)據(jù)和預(yù)期結(jié)果不同的問題。實(shí)際上就是同一測(cè)試腳本使用不同的測(cè)試數(shù)據(jù)來反復(fù)執(zhí)行(但腳本只需要寫一個(gè)),測(cè)試數(shù)據(jù)和測(cè)試行為完全分離。
DDT中心思想:

  1. 將測(cè)試數(shù)據(jù)分離出來,單獨(dú)維護(hù);
  2. 減少重復(fù)自動(dòng)化用例的數(shù)量。

將以上兩種思想進(jìn)行結(jié)合,就可以做成?對(duì)象、數(shù)據(jù)、業(yè)務(wù)行為 三者分離的模型,再結(jié)合模塊進(jìn)行管理,為后續(xù)自動(dòng)化用例腳本的長(zhǎng)期維護(hù)打下基礎(chǔ)。否則時(shí)間一長(zhǎng)自動(dòng)化就會(huì)亂成一團(tuán),維護(hù)成本越來越高,陷入自動(dòng)化率不升反降的怪圈。

關(guān)鍵字驅(qū)動(dòng)(Keyword Driven Testing),在前面的基礎(chǔ)上,可以進(jìn)一步實(shí)現(xiàn)關(guān)鍵字驅(qū)動(dòng)。即將業(yè)務(wù)邏輯相同的部分,抽象成關(guān)鍵字庫(kù)。這樣在寫自動(dòng)化用例腳本時(shí),只需要寫關(guān)鍵字和對(duì)應(yīng)測(cè)試數(shù)據(jù)即可,可以進(jìn)一步減少工作量,減少測(cè)試人員對(duì)代碼的學(xué)習(xí)和依賴。
如京東搜索商品時(shí)直接寫腳本需要好多步:

  1. 定位到搜索框
  2. 輸入關(guān)鍵字
  3. 定位到搜索按鈕
  4. 點(diǎn)擊搜索按鈕
  5. 定位結(jié)果列表
  6. 獲取結(jié)果并返回

以關(guān)鍵字驅(qū)動(dòng)的思想,即將這6步抽象出一個(gè)方法jd_search(),測(cè)試人員只需要寫一句話就能完成以上所有動(dòng)作獲得結(jié)果。如:

result = jd_search('電腦')

方便、省時(shí)省力,測(cè)試人員可聚焦于產(chǎn)品業(yè)務(wù),而不是自動(dòng)化腳本和語言學(xué)習(xí)。
甚至可以直接在設(shè)計(jì)測(cè)試用例的時(shí)候?qū)戧P(guān)鍵字,由自動(dòng)化平臺(tái)去解析用例,都不需要寫腳本。這方面最有名的自動(dòng)化框架就是RobotFrameWork。但是RobotFrameWork過于笨重。建議大家適當(dāng)抽象即可,不要過度抽象。

框架技術(shù)選擇

大多數(shù)框架采用java語言或是python語言來實(shí)現(xiàn),考慮到python容易掌握,各種庫(kù)也比較全,所以采用python語言來實(shí)現(xiàn)。
python自動(dòng)化框架最常用的有unittest和pytest,兩者都可以,這里采用python自帶的unittest。
對(duì)于WEB UI自動(dòng)化測(cè)試,沒有別的選擇,基本都是采用selenium來驅(qū)動(dòng)瀏覽器來完成。
對(duì)于接口自動(dòng)化測(cè)試,可采用的辦法較多,postman、jmeter都可以,但靈活性都不如直接采用python的request庫(kù)。
數(shù)據(jù)驅(qū)動(dòng),由于unittest沒有直接可用的dataprovider,采用常見的ddt來實(shí)現(xiàn)。
對(duì)于手機(jī)自動(dòng)化,暫未實(shí)現(xiàn),后續(xù)考慮加入,可采用appnium來實(shí)現(xiàn)。
測(cè)試數(shù)據(jù),第1階段采用excel管理,對(duì)于大型系統(tǒng),建議直接采用數(shù)據(jù)庫(kù)進(jìn)行管理。
所以總的來講,這個(gè)所謂的框架,就是東拼本湊,即沒有新思想,也沒有新技術(shù),只是將一些常用的技術(shù),按一定的思路組織起來、驅(qū)動(dòng)起來而已。

框架運(yùn)行結(jié)果

總共執(zhí)行6個(gè)用例,4個(gè)為京東搜索并抓取結(jié)果(WEB UI自動(dòng)化測(cè)試),2個(gè)為百度翻譯通用接口(接口自動(dòng)化測(cè)試)。

各用例對(duì)應(yīng)的定義方式(PO/DDT)

頁面定義方式
PO對(duì)象定義:京東主頁面定義了搜索框和搜索按鈕,以name為關(guān)鍵字,定義元素定位方式和執(zhí)行的動(dòng)作。

page_url = 'https://www.jd.com'

elements = [
    {'name': 'search_ipt', 'desc': '搜索框', 'by': (By.ID, u'key'), 'action': 'send_keys()'},
    {'name': 'search_btn', 'desc': '搜索按鈕', 'by': (By.CLASS_NAME, u'button'), 'action': 'click()'},
]

測(cè)試數(shù)據(jù)定義方式

python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試

API接口定義方式
直接采用大家接口測(cè)試時(shí)熟悉的json格式來定義。

# 接口地址信息
uri_scheme = 'http'
endpoint = 'api.fanyi.baidu.com'
resource_path = '/api/trans/vip/translate'
url = uri_scheme + u'://' + endpoint + resource_path

# 保持不變的參數(shù)
_from = 'en'
_to = 'zh'

# 請(qǐng)求消息參數(shù)模板
req_param = {
    "q": "",  # 請(qǐng)求翻譯 query, UTF-8
    "from": _from,  # 翻譯源語言
    "to": _to,  # 翻譯目標(biāo)語言
    "appid": "",  # APP ID
    "salt": "",  # 隨機(jī)數(shù)
    "sign": "",  # 簽名,app_id+q+salt+密鑰 的MD5值
}

# 響應(yīng)消息參數(shù)模板
res_param = {
    "from": _from,
    "to": _to,
    "trans_result": [
        {
            "src": "Hello World! This is 1st paragraph.",
            "dst": "你好,世界!這是第一段。"
        },
        {
            "src": "This is 2nd paragraph.",
            "dst": "這是第二段。"
        }
    ]
}

對(duì)應(yīng)的請(qǐng)求消息頭headers等內(nèi)容也可以定義在這里面。

主程序main.py
負(fù)責(zé)掃描用例,執(zhí)行用例,并生成測(cè)試報(bào)告,發(fā)送郵件。

python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試

測(cè)試執(zhí)行結(jié)果

3個(gè)腳本,每個(gè)腳本2條測(cè)試數(shù)據(jù),共6個(gè)用例。運(yùn)行main.py,執(zhí)行測(cè)試,測(cè)試結(jié)果如下,3個(gè)失敗的是故意修改了測(cè)試數(shù)據(jù)。

紅線部分為接口測(cè)試時(shí),自動(dòng)比對(duì)的json差異,預(yù)期結(jié)果為“蘋果”,實(shí)際結(jié)果為“期望值”。

測(cè)試報(bào)告郵件:

?測(cè)試報(bào)告詳情:

python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試

從零開始搭建項(xiàng)目

一、開發(fā)環(huán)境搭建

  1. 開發(fā)IDE: pycharm?
  2. python: python 3?
  3. 依賴庫(kù):anaconda 3(個(gè)人比較懶,懶得一個(gè)一個(gè)庫(kù)的安裝,這個(gè)庫(kù)比較全)?

基本上都是直接上對(duì)應(yīng)官網(wǎng),下載安裝。準(zhǔn)備好了以后,直接開干。

二、新建項(xiàng)目

pycharm上新建項(xiàng)目TestFrame,選擇好存放目錄,并在TestFrame項(xiàng)目下新建各模塊。注意除了Log和Report是新建Directory外,其它的都是新建Python Package,因?yàn)橄旅孢€要放py文件的。

pycharm上切換項(xiàng)目的python環(huán)境為anaconda,F(xiàn)ile—>Settings—>Project下面切換,如下圖:

python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?

三、基礎(chǔ)功能實(shí)現(xiàn)

1. 配置功能實(shí)現(xiàn)(Conf)

配置功能是項(xiàng)目的基礎(chǔ),所以先實(shí)現(xiàn)。在Conf目錄下新建2個(gè)文件,分別為config.ini和config.py。

config.ini內(nèi)容如下:

[sys]
base_url = https://www.jd.com

[smtp]
host = smtp.163.com
port = 465
user = example@163.com
passwd = password

暫時(shí)先加這么多,后續(xù)需要再慢慢添加。

config.py文件實(shí)現(xiàn)config.ini文件的讀取。
ini文件讀取,python有ConfigParser庫(kù)可以使用,那就直接用。

ConfigParser庫(kù)傳送門

但是每次取值都要用他的方法,比較麻煩,因此對(duì)它的方法進(jìn)行了一個(gè)繼承和改寫,直接將配置文件中所有內(nèi)容讀出來字典形式,方便后續(xù)使用。
代碼如下:

import os
from configparser import ConfigParser
# 使用相對(duì)目錄確定文件位置
_conf_dir = os.path.dirname(__file__)
_conf_file = os.path.join(_conf_dir, 'config.ini')

# 繼承ConfigParser,寫一個(gè)將結(jié)果轉(zhuǎn)為dict的方法
class MyParser(ConfigParser):
    def as_dict(self):
        d = dict(self._sections)
        for k in d:
            d[k] = dict(d[k])
        return d

# 讀取所有配置,以字典方式輸出結(jié)果
def _get_all_conf():
    _config = MyParser()
    result = {}
    if os.path.isfile(_conf_file):
        try:
            _config.read(_conf_file, encoding='UTF-8')
            result = _config.as_dict()
        except OSError:
            raise ValueError("Read config file failed: %s" % OSError)
    return result

# 將各配置讀取出來,放在變量中,后續(xù)其它文件直接引用這個(gè)這些變量
config = _get_all_conf()
sys_cfg = config['sys']
smtp_cfg = config['smtp']

print(sys_cfg)
print(smtp_cfg)
print(smtp_cfg['host'])

運(yùn)行結(jié)果:

{'base_url': 'https://www.jd.com'}
{'host': 'smtp.163.com', 'port': '465', 'user': 'example@163.com', 'passwd': 'password'}
smtp.163.com

后續(xù)其它文件就可以直接使用 sys_cfg 和 smtp_cfg 這兩個(gè)字典,以key的方式訪問需要的配置內(nèi)容。

2. 日志功能實(shí)現(xiàn)(Log)

日志在項(xiàng)目中也是基礎(chǔ)功能,所以接著做日志。
python自帶logging庫(kù),可以定制日志的格式,就直接使用該庫(kù)實(shí)現(xiàn),沒必要自己造。

先去我們的配置文件中config.ini添加日志相關(guān)的配置,這里先定義3個(gè)配置:日志級(jí)別、日志格式、日志路徑。

[log]
log_level = logging.DEBUG
log_format = %(asctime)s - %(name)s - %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s
log_path = Log

再在config.py中最后面添加一行代碼,把log相關(guān)的配置放在一個(gè)變量中,好直接使用。

log_cfg = config['log']
print(smtp_cfg)

打印出來看一下結(jié)果:

{'log_level': 'logging.DEBUG', 'log_format': '%(asctime)s - %(name)s - %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s', 'log_path': 'Log'}

日志級(jí)別有:DEBUG、INFO、WARN、ERROR、FATAL。一般調(diào)試都是DEBUG,上線就改為INFO。
這里簡(jiǎn)單介紹一下日志格式log_format的內(nèi)容:

參數(shù) 意義 說明
asctime 時(shí)間 格式:2021-03-14 09:37:40,258
name logger的名稱 簡(jiǎn)單理解就是將來把模塊名稱填到這里,區(qū)分是誰打的日志
filename 文件名 哪個(gè)文件打印的這條日志
line 行號(hào) 哪一行打印的這條日志
levelname 級(jí)別 日志的級(jí)別,注意是級(jí)別的name
message 內(nèi)容 我們打印的日志內(nèi)容
log_path 日志文件 保存到哪個(gè)日志文件

再接著在Comm目錄下,新建一個(gè)Log.py,開始定制日志。定制日志還有幾個(gè)問題要提前考慮:
一是存放目錄問題,我們這里使用了固定目錄,所以問題不大。
二是日志分割、滾動(dòng)問題,每天跑持續(xù)集成,大量用例生成大量日志,日志堆成山。如果覺得日志有用呢,就搞個(gè)ELK把日志取走存放起來做分析。如果覺得日志沒用呢,保存幾天后就刪除掉。無論怎么講,都要實(shí)現(xiàn)日志的分割和滾動(dòng)。
幸好你想到的大佬們?cè)缇拖氲搅耍琹ogging模塊就有這個(gè)功能,只要配置一下就可以了。

下面開搞,引入logging庫(kù),把項(xiàng)目的根路徑取出來,把上面config.ini中的日志配置取過來,最后拼接好日志文件存放的絕對(duì)路徑:

import os
import logging
from Conf.Config import log_cfg

_BaseHome = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

_log_level = eval(log_cfg['log_level'])
_log_path = log_cfg['log_path']
_log_format = log_cfg['log_format']

_log_file = os.path.join(_BaseHome, _log_path, 'log.txt')

注意上面log_level的寫法,這里用了個(gè)eval,如果不加這個(gè)函數(shù),log_level取過來是個(gè)字符串,沒法直接用,通過eval執(zhí)行后,就變成了logging定義的對(duì)象了。

再配置日志,引入TimedRotatingFileHandler這個(gè)東東,這是實(shí)現(xiàn)滾動(dòng)日志的。

from logging.handlers import TimedRotatingFileHandler

def log_init():
    logger = logging.getLogger('main')
    logger.setLevel(level=_log_level)
    formatter = logging.Formatter(_log_format)

    handler = TimedRotatingFileHandler(filename=_log_file, when="D", interval=1, backupCount=7)
    handler.setLevel(_log_level)
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    console = logging.StreamHandler()
    console.setLevel(_log_level)
    console.setFormatter(formatter)
    logger.addHandler(console)

這個(gè)日志里面,加了兩個(gè)輸出,handler用于向日志文件打印日志,console 用于向終端打印日志,兩個(gè)的定義方式不同。
TimedRotatingFileHandler的參數(shù)簡(jiǎn)介:

參數(shù) 意義 說明
filename 日志文件 沒啥好說的
when 切割條件 按周(W)、天(D)、時(shí)(H)、分(M)、秒(S)切割
interval 間隔 就是幾個(gè)when切割一次。when是W,interval是3的話就代表3周切割一次
backupCount 日志備份數(shù)量 就是保留幾個(gè)日志文件,起過這個(gè)數(shù)量,就把最早的刪除掉,從而滾動(dòng)刪除

我這里配置的是每天生成1個(gè)日志文件,保留7天的日志。

日志就做好了,試一下效果。

log_init()
logger = logging.getLogger('main')
logger.info('log test----------')

運(yùn)行結(jié)果:

2021-03-15 21:53:41,972 - main - Log.py[line:49] - INFO - log test----------

其它文件使用日志:
先在main.py里面引入這個(gè)log_init(),在最開始的時(shí)候初始化一下,日志就配置好了。
再在各個(gè)要使用日志的文件中,直接按下面這種方式使用:

import logging
logger = logging.getLogger('main.jd')

注意各個(gè)模塊自己getLogger的時(shí)候,直接main后面加上“.模塊名”,就能使用同一個(gè)logger區(qū)分模塊了。

到這里日志功能就完成了。

順手做個(gè)截圖的功能,供大家使用。截圖可以直接在用例里面用selenium提供的截圖功能,也可以自己做一個(gè)公共的。下面是用PIL里面的功能做的截圖。

from PIL import ImageGrab

# 先定義截圖文件的存放路徑,這里在Log目錄下建個(gè)Screen目錄,按天存放截圖
_today = time.strftime("%Y%m%d")
_screen_path = os.path.join(_BaseHome, _log_path, 'Screen', _today)

#再使用PIL的ImageGrab實(shí)現(xiàn)截圖
def screen(name):
    t = time.time()
    png = ImageGrab.grab()
    if not os.path.exists(_screen_path):
        os.makedirs(_screen_path)
    image_name = os.path.join(_screen_path, name)
    png.save('%s_%s.png' % (image_name, str(round(t * 1000))))  # 文件名后面加了個(gè)時(shí)間戳,避免重名

運(yùn)行這個(gè)方法就能截圖了,大功告成。截圖文件其實(shí)也需要一個(gè)滾動(dòng)刪除,后面有時(shí)間再寫吧。

3. 讀取EXCEL實(shí)現(xiàn)(data)

接著寫一個(gè)讀取EXCEL文件數(shù)據(jù)的功能吧,這個(gè)項(xiàng)目里面主要是用來讀測(cè)試數(shù)據(jù),以實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)。
python讀取excel數(shù)據(jù),我看大家都喜歡用xlrd和xlwt,還有用openpyxl的,對(duì)于我這種懶人來講,都太麻煩了。
我們用pandas來干,一句話的事情,搞那么多干嗎,用python就是要快。

在Comm目錄下,新建一個(gè)data.py,專門來處理數(shù)據(jù)。引入pandas,直接用pandas的read_excel讀excel,而且支持它原始的其它參數(shù),只是最后將結(jié)果轉(zhuǎn)了字典,方便使用:

import pandas as pd

def read_excel(file, **kwargs):
    data_dict = []
    try:
        data = pd.read_excel(file, **kwargs)
        data_dict = data.to_dict('records')
    finally:
        return data_dict

隨便放一個(gè)excel在同一個(gè)目錄下,填上數(shù)據(jù),試一下效果。excel里面2頁數(shù)據(jù),Sheet1如下:
python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?
Sheet2如下:?

?python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?

調(diào)用我們寫好的方法,打印數(shù)據(jù):

sheet1 = read_excel('baidu_fanyi.xlsx')
sheet2 = read_excel('baidu_fanyi.xlsx', sheet_name='Sheet2')
print(sheet1)
print(sheet2)

運(yùn)行結(jié)果如下:

[{'req.q': '計(jì)算機(jī)\n計(jì)算機(jī)', 'req.from': 'zh', 'req.to': 'en', 'res.from': 'zh', 'res.to': 'en', 'res.trans_result.0.src': '計(jì)算機(jī)', 'res.trans_result.0.dst': 'computer', 'res.trans_result.1.src': '計(jì)算機(jī)', 'res.trans_result.1.dst': 'computer'}, 
 {'req.q': 'computer\nexpected value', 'req.from': 'en', 'req.to': 'zh', 'res.from': 'en', 'res.to': 'zh', 'res.trans_result.0.src': 'computer', 'res.trans_result.0.dst': '計(jì)算機(jī)', 'res.trans_result.1.src': 'expected value', 'res.trans_result.1.dst': '蘋果'}]

[{'req.q': '計(jì)算機(jī)', 'req.from': 'zh', 'req.to': 'en', 'res.from': 'zh', 'res.to': 'en'}, 
{'req.q': 'computer', 'req.from': 'en', 'req.to': 'zh', 'res.from': 'en', 'res.to': 'zh'}]

每頁數(shù)據(jù)都讀出來了,而且每一行都是字典形式,直接通過key就可以方便的使用。

pandas還能直接計(jì)算數(shù)據(jù),如通過幾個(gè)列算加密簽名,寫動(dòng)態(tài)cookie等,使用方法也很簡(jiǎn)單。比如在數(shù)據(jù)中增加一列sign, 讓它簡(jiǎn)單等于 req.from列 + ‘.aaaa.’ + req.to列,給大家演示一下。

data = pd.read_excel('baidu_fanyi.xlsx')
data['sign'] = data["req.from"] +'.aaaaa.' + data["req.to"]
data_dict = data.to_dict('records')
print(data_dict)

運(yùn)行結(jié)果:

[{'req.q': '計(jì)算機(jī)\n計(jì)算機(jī)', 'req.from': 'zh', 'req.to': 'en', 'res.from': 'zh', 'res.to': 'en', 'res.trans_result.0.src': '計(jì)算機(jī)', 'res.trans_result.0.dst': 'computer', 'res.trans_result.1.src': '計(jì)算機(jī)', 'res.trans_result.1.dst': 'computer', 'sign': 'zh.aaaaa.en'}, 
{'req.q': 'computer\nexpected value', 'req.from': 'en', 'req.to': 'zh', 'res.from': 'en', 'res.to': 'zh', 'res.trans_result.0.src': 'computer', 'res.trans_result.0.dst': '計(jì)算機(jī)', 'res.trans_result.1.src': 'expected value', 'res.trans_result.1.dst': '蘋果', 'sign': 'en.aaaaa.zh'}]

我們可以看到多了一列sign,值就是自動(dòng)根據(jù)每一行的數(shù)據(jù)算出來的,這對(duì)于我們數(shù)據(jù)驅(qū)動(dòng)來講,去計(jì)算一些動(dòng)態(tài)值非常有用。我這里沒有用到動(dòng)態(tài)的,只是讀而已。大家如果要計(jì)算,就要自己寫計(jì)算方法。

pandas還支持直接讀各種主流數(shù)據(jù)庫(kù),后面擴(kuò)展也很方便,我們一直都用它。

4. 郵件發(fā)送實(shí)現(xiàn)(Email)

實(shí)現(xiàn)郵件功能,用于發(fā)送測(cè)試報(bào)告。使用python的smtplib模塊實(shí)現(xiàn)。

先在Conf目錄下的config.ini中添加好郵件相關(guān)的配置:

[smtp]
host = smtp.163.com
port = 465
user = example@163.com
passwd = password

[email]
sender = example@163.com
receivers = example@qq.com, example@163.com

再在Config.py中將它們?nèi)〉阶兞恐蟹藕茫?/p>

smtp_cfg = config['smtp']
email_cfg = config['email']

然后在Comm目錄下新建Email.py,開始擼代碼。郵件支持了定義主題、正文和多個(gè)附件,控制了單個(gè)附件大小和附件總數(shù)。代碼如下:

import smtplib
import os
import logging
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.header import Header
from Conf.Config import smtp_cfg, email_cfg

_FILESIZE = 20  # 單位M, 單個(gè)附件大小
_FILECOUNT = 10  # 附件個(gè)數(shù)
_smtp_cfg = smtp_cfg
_email_cfg = email_cfg
_logger = logging.getLogger('main.email')


class Email:
    def __init__(self, subject, context=None, attachment=None):
        self.subject = subject
        self.context = context
        self.attachment = attachment
        self.message = MIMEMultipart()
        self._message_init()

    def _message_init(self):
        if self.subject:
            self.message['subject'] = Header(self.subject, 'utf-8')  # 郵件標(biāo)題
        else:
            raise ValueError("Invalid subject")

        self.message['from'] = _email_cfg['sender']  # from
        self.message['to'] = _email_cfg['receivers']  # to

        if self.context:
            self.message.attach(MIMEText(self.context, 'html', 'utf-8'))  # 郵件正文內(nèi)容
        # 郵件附件
        if self.attachment:
            if isinstance(self.attachment, str):
                self._attach(self.attachment)
            if isinstance(self.attachment, list):
                count = 0
                for each in self.attachment:
                    if count <= _FILECOUNT:
                        self._attach(each)
                        count += 1
                    else:
                        _logger.warning('Attachments is more than ', _FILECOUNT)
                        break

    def _attach(self, file):
        if os.path.isfile(file) and os.path.getsize(file) <= _FILESIZE * 1024 * 1024:
            attach = MIMEApplication(open(file, 'rb').read())
            attach.add_header('Content-Disposition', 'attachment', filename=os.path.basename(file))
            attach["Content-Type"] = 'application/octet-stream'
            self.message.attach(attach)
        else:
            _logger.error('The attachment is not exist or more than %sM: %s' % (_FILESIZE, file))

    def send_mail(self):
        s = smtplib.SMTP_SSL(_smtp_cfg['host'], int(_smtp_cfg['port']))
        result = True
        try:
            s.login(self._smtp_cfg['user'], self._smtp_cfg['passwd'])
            s.sendmail(self._smtp_cfg['sender'], self._smtp_cfg['receivers'], self.message.as_string())
        except smtplib.SMTPException as e:
            result = False
            _logger.error('Send mail failed', exc_info=True)
        finally:
            s.close()
        return result

郵件初始化發(fā)送時(shí)的調(diào)用方式如下:

mail = Email(title, context, file)
send = mail.send_mail()
print(send)

返回結(jié)果為True則發(fā)送成功,否則發(fā)送失敗。

四、WEB UI自動(dòng)化

WEB UI自動(dòng)化,采用 selenium來完成。通過PO對(duì)象、測(cè)試數(shù)據(jù)、業(yè)務(wù)邏輯三者分離的方式來實(shí)現(xiàn)。
另外一個(gè)主旨是盡量讓測(cè)試人員使用selenium原生的各種方法,而不要做過多封裝。原因很簡(jiǎn)單,不要讓測(cè)試人員來學(xué)這個(gè)框架,而是去學(xué)selenium,這樣以后他出去換工作才有飯吃。如果過度封裝,就會(huì)讓測(cè)試人員來學(xué)這個(gè)框架,他以后出去selenium都不會(huì)用,這不是害了別人么。框架的目的只是把對(duì)象、數(shù)據(jù)、業(yè)務(wù)邏輯三者驅(qū)動(dòng)起來,讓測(cè)試人員工作起來更快。

我們以京東搜索爬蟲為例來看如何構(gòu)建這三者的關(guān)系:在京東主頁面,搜索“電腦”,再獲取搜索結(jié)果,保存。

1. 頁面PO對(duì)象配置

打開京東商城主頁,找到搜索框元素、和搜索按鈕元素,分別確定他們的定位方式,以及元素對(duì)應(yīng)的操作。
然后建立這個(gè)頁面對(duì)象,在Page下新建一個(gè)名為"jd"的python package,再在這個(gè)package下新建一個(gè)jd.py,用來定義京東商城的主頁面對(duì)象。

from selenium.webdriver.common.by import By


page_url = 'https://www.jd.com'

elements = [
    {'name': 'search_ipt', 'desc': '搜索框點(diǎn)擊', 'by': (By.ID, u'key'), 'ec': 'presence_of_element_located', 'action': 'send_keys()'},
    {'name': 'search_btn', 'desc': '搜索按鈕點(diǎn)擊', 'by': (By.CLASS_NAME, u'button'), 'ec': 'presence_of_element_located', 'action': 'click()'},
]

name: 每個(gè)元素+操作的唯一標(biāo)識(shí)。一個(gè)元素可能由于操作不同,而要定義多個(gè),但大部分只要定義一個(gè)。
desc:元素+操作的描述。
by:元素的定位方式,使用selenium的原生定位方式,不自己定義封裝。
ec: 等待元素出現(xiàn)的方式,這個(gè)暫時(shí)未用。
action:元素的對(duì)應(yīng)操作。使用原生的selenium動(dòng)作方法,不自己定義封裝。
京東商城主頁面現(xiàn)在只用到這兩個(gè),就只定義這兩個(gè)。

搜索結(jié)果頁面,定義如下:

from selenium.webdriver.common.by import By

page_url = 'https://search.jd.com/'

elements = [
    {'name': 'result_list', 'desc': '結(jié)果列表', 'by': (By.CLASS_NAME, u'gl-item'), 'ec': 'presence_of_all_elements_located', 'action': None},
    {'name': 'price', 'desc': '價(jià)格', 'by': (By.XPATH, u".//div[@class='p-price']/strong/i"), 'ec': 'presence_of_element_located', 'action': 'text'},
    {'name': 'pname', 'desc': '描述', 'by': (By.XPATH, u".//div[@class='p-name p-name-type-2']/a/em"), 'ec': 'presence_of_element_located', 'action': 'text'}
]

2. 實(shí)現(xiàn)basePage基類

basePage基類的實(shí)現(xiàn)思想是不做過多的封裝,盡量讓測(cè)試人員直接使用selenium原裝的方法,而不像其它框架一樣什么都封裝在這里面。
所以我對(duì)basePage的定義是:根據(jù)業(yè)務(wù)邏輯(測(cè)試用例)指定的元素,輸入的數(shù)據(jù),協(xié)助它完成元素定位和操作,僅此而已。
當(dāng)然如果去封裝各種東西也是可以的,直接在里面加就行了。

在Page目錄下,新建basePage.py,開始擼代碼:

from selenium.webdriver.common.by import By
from selenium import webdriver
import os
import importlib
import logging

SimpleActions = ['clear()', 'send_keys()', 'click()', 'submit()', 'size', 'text', 'is_displayed()', 'get_attribute()']
logger = logging.getLogger('main.page')


class Page(object):

    def __init__(self, driver, page):
        self.driver = driver
        self.page = page
        self.elements = get_page_elements(page)
        self.by = ()
        self.action = None

    def _get_page_elem(self, elem):
        # 獲取定位元素的 by,以及操作action
        for each in self.elements:
            if each['name'] == elem:
                self.by = each['by']
                if 'action' in each and each['action'] is not None:
                    self.action = each['action']
                else:
                    self.action = None

    def oper_elem(self, elem, args=None):
        self._get_page_elem(elem)
        cmd = self._selenium_cmd('find_element', args)
        return eval(cmd)

    def oper_elems(self, elem, args=None):
        self._get_page_elem(elem)
        cmd = self._selenium_cmd('find_elements', args)
        return eval(cmd)

    def _selenium_cmd(self, find_type='find_element', args=None):
        # 拼接 selenium 查找命令, 查找單個(gè)元素時(shí)find_type為'find_element',多個(gè)元素時(shí)為'find_elements'
        cmd = 'self.driver.' + find_type + '(*self.by)'
        if self.action:
            if self.action in SimpleActions:
                cmd = cmd + '.' + self.action
                if args:
                    cmd = cmd[:-1] + 'args' + ')'
        return cmd

def get_page_elements(page):
    """動(dòng)態(tài)加載頁面定義文件,獲取文件中定義的元素列表elements"""
    elements = None
    if page:
        try:
            m = importlib.import_module(page)
            elements = m.elements
        except Exception as e:
            logger.error('error info : %s' %(e))
    return elements

?這里面主要的只包含3個(gè)方法,一個(gè)是動(dòng)態(tài)加載指定的PO對(duì)象獲取元素列表,一個(gè)是在獲取的元素列表中去找到當(dāng)前要操作的元素,最后一個(gè)就是拼接原生的selenium命令,將測(cè)試數(shù)據(jù)插入到動(dòng)作里面去。
其它的就簡(jiǎn)單了,直接調(diào)用selenium運(yùn)行拼接出來的命令,把結(jié)果返回出去。
這里要注意的是,有些復(fù)雜的selenium操作,不能這么簡(jiǎn)單的拼命令,要特殊處理,這里暫時(shí)沒弄;簡(jiǎn)單的命令,也沒有列全。后面再慢慢加。

3. 寫業(yè)務(wù)測(cè)試用例

下面開始寫測(cè)試用例。
在Testcase目錄下,新建一個(gè)python package:Model1。在Model1下面再建一個(gè)目錄:Testdata,用于放測(cè)試數(shù)據(jù);建一個(gè)python package:Case,用于放用例腳本。目錄結(jié)構(gòu)如下:
python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?

準(zhǔn)備測(cè)試數(shù)據(jù):
準(zhǔn)備一份excel數(shù)據(jù)(test_jd_desktop.xlsx),存放在Model1/Testdata/jd下:
python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?
keyword:搜索的關(guān)鍵字
count:搜索結(jié)果總數(shù),只抓了一頁,應(yīng)該是60個(gè)

實(shí)現(xiàn)業(yè)務(wù)用例:
在Model1/Case/jd下新建一個(gè)文件:test_jd_desktop.py,開始寫用例腳本。
用例使用unittest結(jié)合DDT來實(shí)現(xiàn),具體代碼如下:

import os
import unittest
import ddt
import logging
from selenium import webdriver
from time import sleep
from Page.basePage import Page
from Comm.Log import screen
from Comm.data import read_excel
from main import TestCasePath

logger = logging.getLogger('main.jd')
# 讀取測(cè)試數(shù)據(jù)
file = os.path.join(TestCasePath, 'Model1/Testdata/jd/test_jd_desktop.xlsx')
test_data = read_excel(file)

PO_jd = 'Page.jd.jd'
PO_search = 'Page.jd.search_jd'

@ddt.ddt  # 數(shù)據(jù)驅(qū)動(dòng)
class TestJdSearchDesktop(unittest.TestCase):
    """京東搜索測(cè)試"""

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.count = 0
        self.result = []

    @ddt.data(*test_data) # 數(shù)據(jù)驅(qū)動(dòng)傳具體數(shù)據(jù)
    def testJdSearchDesktop(self, test_data):
        """京東搜索測(cè)試--電腦"""
        url = 'https://www.jd.com'
        keyword = test_data['keyword']
        wait = self.driver.implicitly_wait(5)

        try:
            self.driver.get(url)
            # 實(shí)例化jd主頁面
            jd = Page(self.driver, PO_jd)
            # 實(shí)例化jd搜索結(jié)果頁面
            jd_search = Page(self.driver, PO_search)
            wait
            # jd主頁面的搜索框元素中輸入關(guān)鍵字
            jd.oper_elem('search_ipt', keyword)
            wait
            # 操作jd主頁面的搜索按鈕元素
            jd.oper_elem('search_btn')

            sleep(1)
            self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            sleep(1)

			# jd搜索結(jié)果頁面,獲取結(jié)果列表
            lis = jd_search.oper_elems('result_list')

			# 在取到的結(jié)果列表中,循環(huán)獲取商品價(jià)格和商品名稱,結(jié)果存EXCEL就沒寫了
            for each in lis:
                self.count += 1
                page_each = Page(each, PO_search)
                price = page_each.oper_elem('price')
                name = page_each.oper_elem('pname')
                self.result.append([name, price])

            sleep(1)

        except Exception as E:
            logger.error('error info : %s' % (E))
            screen(test_data['keyword'])

		# 判斷是不是取到了60個(gè)商品
        self.assertEqual(test_data['count'], self.count)

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

五、實(shí)現(xiàn)主程序

主程序的主要作用是 組織用例,執(zhí)行用例,生成報(bào)告,發(fā)送測(cè)試報(bào)告郵件。
組織用例和執(zhí)行用例都直接用unittest;
生成報(bào)告,采用BeautifulReport;
下面開始擼main.py的代碼:

import unittest
import os
import time
import logging
from Comm.Email import Email
from Comm.Log import log_init
from BeautifulReport import BeautifulReport

# 定義各目錄
ProjectHome = os.path.split(os.path.realpath(__file__))[0]
PageObjectPath = os.path.join(ProjectHome, "Page")
TestCasePath = os.path.join(ProjectHome, "Testcase")
ReportPath = os.path.join(ProjectHome, "Report")

#對(duì)測(cè)試結(jié)果關(guān)鍵信息進(jìn)行匯總,做為郵件正文
def summary_format(result):
    summary = "\n" + u"<p>          測(cè)試結(jié)果匯總信息                </p>" + "\n" + \
                 u"<p> 開始時(shí)間: " + result['beginTime'] + u" </p>" + "\n" + \
                 u"<p> 運(yùn)行時(shí)間: " + result['totalTime'] + u" </p>" + "\n" + \
                 u"<p> 執(zhí)行用例數(shù): " + str(result['testAll']) + u" </p>" + "\n" + \
                 u"<p> 通過用例數(shù): " + str(result['testPass']) + u" </p>" + "\n" + \
                 u"<p> 失敗用例數(shù): " + str(result['testFail']) + u" </p>" + "\n" + \
                 u"<p> 忽略用例數(shù): " + str(result['testSkip']) + u" </p>" + "\n"
    return summary

# 發(fā)送郵件
def send_email(file, context):
    title = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '自動(dòng)化測(cè)試結(jié)果'
    mail = Email(title, context, file)
    send = mail.send_mail()
    if send:
        print('測(cè)試報(bào)告郵件發(fā)送成功')
    else:
        print('測(cè)試報(bào)告郵件發(fā)送失敗')

# 加載測(cè)試用例
def get_suite(case_path=TestCasePath, rule="test_*.py"):
    """加載所有的測(cè)試用例"""
    unittest_suite = unittest.TestSuite()
    discover = unittest.defaultTestLoader.discover(case_path, pattern=rule, top_level_dir=None)
    for each in discover:
        unittest_suite.addTests(each)
    return unittest_suite

# 執(zhí)行用例,生成測(cè)試報(bào)告,并返回報(bào)告附件路徑、郵件正文內(nèi)容
def suite_run(unittest_suite):
    """執(zhí)行所有的用例, 并把結(jié)果寫入測(cè)試報(bào)告"""
    run_result = BeautifulReport(unittest_suite)

    now = time.strftime("%Y%m%d%H%M%S", time.localtime())
    filename = now + '_report.html'
    run_result.report(filename=filename, description=now, report_dir=ReportPath)
    rpt_summary = summary_format(run_result.fields)
    return os.path.join(ReportPath, filename), rpt_summary

# 主程序,加載用例,執(zhí)行用例,發(fā)送郵件
if __name__ == "__main__":
    suite = get_suite()
    report_file, report_summary = suite_run(suite)
    print(report_summary)
    send_email(report_file, report_summary)

API自動(dòng)化,采用 request庫(kù)來完成。還是通過PO對(duì)象、測(cè)試數(shù)據(jù)、業(yè)務(wù)邏輯三者分離的方式來實(shí)現(xiàn)。
這里以百度通用翻譯接口為例,這個(gè)接口對(duì)個(gè)人用戶是免費(fèi)的,大家可以自己去申請(qǐng)。

1. API對(duì)象配置

在APIs下面新建python package:fanyi,再在fanyi下面建baidu.py。
將百度通用翻譯接口定義在這里面,直接采用大家熟悉的json格式:

"""百度通用翻譯接口""" 
API_NAME = 'fanyi'
# 地址信息
uri_scheme = 'http'
endpoint = 'api.fanyi.baidu.com'
resource_path = '/api/trans/vip/translate'
url = uri_scheme + u'://' + endpoint + resource_path

# 保持不變的參數(shù)
_from = 'en'
_to = 'zh'

# 請(qǐng)求消息參數(shù)
req_param = {
    "q": "",  # 請(qǐng)求翻譯 query, UTF-8
    "from": _from,  # 翻譯源語言
    "to": _to,  # 翻譯目標(biāo)語言
    "appid": "",  # APP ID
    "salt": "",  # 隨機(jī)數(shù)
    "sign": "",  # 簽名,appid+q+salt+密鑰 的MD5值
}

# 響應(yīng)消息參數(shù)
res_param = {
    "from": _from,
    "to": _to,
    "trans_result": [
        {
            "src": "Hello World! This is 1st paragraph.",
            "dst": "你好,世界!這是第一段。"
        },
        {
            "src": "This is 2nd paragraph.",
            "dst": "這是第二段。"
        }
    ]
}

2.實(shí)現(xiàn)base_api基類

base_api基類,主要是將數(shù)據(jù)、API對(duì)象、測(cè)試用例三者連起來;
在APIs目錄下,新建base_api.py,代碼如下:

import logging
import random
import importlib
import copy
import json
import unittest
from hashlib import md5
from ipaddress import ip_address
from Comm.compare import json_compare


logger = logging.getLogger('main.api')
req_prefix = 'req.'
res_prefix = 'res.'


def _separate_data(data, prefix='req.'):
    pfx = prefix
    result = {}
    for key, value in data.items():
        if key.startswith(pfx):
            req_key = key[len(pfx):]
            result[req_key] = value
    return result


def _get_cmd(key, dict_name='payload'):
    separator = '.'
    cmd = dict_name
    if separator in key:
        data_key = key.split(separator)
        for each in data_key:
            if each.isdigit():
                cmd = cmd + '[' + each + ']'
            else:
                cmd = cmd + '[\'' + each + '\']'
        cmd = cmd + ' = value'
    else:
        cmd = cmd + '[key] = value'
    return cmd


def check_result(unittest_testcase, x, y):
    # 只有x,y完全相同才能通過,任意不同則返回失敗。建議自己在用例中做結(jié)果檢查
    testcase = unittest_testcase
    diff = json_compare(x, y)
    testcase.assertEqual(x, y)


class BaseAPI(object):
    def __init__(self, api):
        self.api = api
        self.api_name = None
        self.url = ''
        self.req_template = {}
        self.res_template = {}
        self._get_api_param()

    def _get_api_param(self):
        """動(dòng)態(tài)加載API定義文件,獲取文件中定義的API參數(shù)"""
        try:
            m = importlib.import_module(self.api)
            self.api_name = m.API_NAME
            self.url = m.url
            self.req_template = m.req_param
            self.res_template = m.res_param
        except Exception as e:
            logger.error('error info : %s' % e)

    def payload(self, data=None):
        payload = copy.deepcopy(self.req_template)
        if data:
            req_pre = '.'.join([self.api_name, req_prefix])
            req_data = _separate_data(data, req_pre)
            for key, value in req_data.items():
                cmd = _get_cmd(key, 'payload')
                exec(cmd)
        return payload

    def load_expected(self, data=None):
        expected = copy.deepcopy(self.res_template)
        if data:
            res_pre = '.'.join([self.api_name, res_prefix])
            res_data = _separate_data(data, res_pre)
            for key, value in res_data.items():
                cmd = _get_cmd(key, 'expected')
                exec(cmd)
        return expected

這里面的思路是:

  1. 動(dòng)態(tài)加載API對(duì)象,獲取API請(qǐng)求參數(shù)模板、和響應(yīng)參數(shù)模板;
  2. payload的時(shí)候,從測(cè)試數(shù)據(jù)中,取出API請(qǐng)求相關(guān)的數(shù)據(jù)(以API名.req開頭,如fanyi.req.q),填入模板,沒有的就用模板數(shù)據(jù);
  3. 加載預(yù)期結(jié)果的時(shí)候,從測(cè)試數(shù)據(jù)中,取出API響應(yīng)相關(guān)的數(shù)據(jù)(以API名.res開頭,如fanyi.res.trans_result.0.src),填入模板,沒有的就用模板數(shù)據(jù)。
  4. 提供json比較的方法;
  5. 提供了一個(gè)隨機(jī)handers。

具體的大家看一下就明白了。想進(jìn)一步封裝的還可以繼續(xù)封裝,比如生成hearders,數(shù)據(jù)配完了直接發(fā)送,取到結(jié)果直接比對(duì)什么的。但是建議不要過度封裝。

附j(luò)son比較的方法:

import json_tools


def json_compare(x, y):
    diff = json_tools.diff(x, y)
    if diff:
        for action in diff:
            if 'add' in action:
                print('++增加元素:', action['add'], ' 值:', action['value'])
            elif 'remove' in action:
                print('--刪除元素:', action['remove'], ' 值:',  action['prev'])
            elif 'replace' in action:
                print('**修改元素:', action['replace'], ' 值:', action['prev'], '-->', action['value'])
    return diff

3.測(cè)試用例

在Testcase下建API模塊,API模塊下建Case和Testdata,分別放用例和數(shù)據(jù),目錄如下:
python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?

定義測(cè)試數(shù)據(jù)
測(cè)試數(shù)據(jù)需要按一定的格式處理,即每個(gè)參數(shù)以api名稱開頭,用“.”連接,然后用res和req來區(qū)分響應(yīng)還是請(qǐng)求,后面就是具體的參數(shù)了,多級(jí)參數(shù)以“.”連接。具體如下:
python自動(dòng)化測(cè)試平臺(tái)開發(fā),自動(dòng)化測(cè)試,自動(dòng)化測(cè)試框架,自動(dòng)化,Python自動(dòng)化測(cè)試,ui,軟件測(cè)試?

測(cè)試用例腳本:
仍然用unittest和ddt來實(shí)現(xiàn)。

import os
import unittest
import ddt
import random
import json
import requests
from time import sleep
from Comm.data import read_excel
from Comm.encryption import make_md5
from main import TestCasePath
from APIs.base_api import BaseAPI, check_result


# 開通普通個(gè)人的百度翻譯接口,設(shè)置appid和appkey.
app_id = your appid
app_key = your appkey

# 獲取測(cè)試數(shù)據(jù)
file = os.path.join(TestCasePath, 'API/TestData/baidu_fanyi.xlsx')
test_data = read_excel(file)
api = 'APIs.fanyi.baidu'


@ddt.ddt
class TestBaiduFanyi(unittest.TestCase):
    """百度翻譯接口測(cè)試"""

    def setUp(self):
        self.api = BaseAPI(api)

    @ddt.data(*test_data)
    def test_baidu_fanyi(self, test_data):
        """百度翻譯接口測(cè)試"""
        api = self.api

        # Build test_data,這是些動(dòng)態(tài)參數(shù),在這里計(jì)算
        test_data['fanyi.req.appid'] = app_id
        salt = random.randint(32768, 65536)
        test_data['fanyi.req.salt'] = salt
        sign = make_md5(app_id + test_data['fanyi.req.q'] + str(salt) + app_key)
        test_data['fanyi.req.sign'] = sign

        # Build request
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        payload = api.payload(test_data )

        # Send request
        r = requests.post(api.url, params=payload, headers=headers)
        result = r.json()
        expected = api.load_expected(test_data)
        self.assertEqual(r.status_code, 200)
        check_result(self, expected, result) # 簡(jiǎn)單的模板驗(yàn)證,大家最好自己寫驗(yàn)證。

        sleep(0.5)

然后運(yùn)行主程序,API自動(dòng)化測(cè)試也就可以跑起來了。

補(bǔ):MD5函數(shù)

from hashlib import md5

def make_md5(s, encoding='utf-8'):
    return md5(s.encode(encoding)).hexdigest()

結(jié)語

這篇貼子到這里就結(jié)束了,最后,希望看這篇帖子的朋友能夠有所收獲。

如果你覺得文章還不錯(cuò),請(qǐng)大家?點(diǎn)贊、分享、留言?下,因?yàn)檫@將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強(qiáng)動(dòng)力!文章來源地址http://www.zghlxwxcb.cn/news/detail-721946.html

到了這里,關(guān)于從零搭建完整python自動(dòng)化測(cè)試框架(UI自動(dòng)化和接口自動(dòng)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Python Selenium搭建UI自動(dòng)化測(cè)試框架

    自動(dòng)化測(cè)試是軟件測(cè)試中非常重要的一部分,可以提高測(cè)試效率和測(cè)試覆蓋率。在UI自動(dòng)化測(cè)試中,Selenium是非常流行的工具。本文將介紹如何使用Python和Selenium搭建UI自動(dòng)化測(cè)試框架。 在開始搭建UI自動(dòng)化測(cè)試框架之前,需要先安裝Python和Selenium。可以從Python官網(wǎng)下載Python安裝

    2023年04月27日
    瀏覽(21)
  • Python+selenium,輕松搭建 Web 自動(dòng)化測(cè)試框架

    Python+selenium,輕松搭建 Web 自動(dòng)化測(cè)試框架

    在程序員的世界中,一切重復(fù)性的工作,都應(yīng)該通過程序自動(dòng)執(zhí)行。 「自動(dòng)化測(cè)試」就是一個(gè)最好的例子。 隨著互聯(lián)網(wǎng)應(yīng)用開發(fā)周期越來越短,迭代速度越來越快,只會(huì)點(diǎn)點(diǎn)點(diǎn),不懂開發(fā)的手工測(cè)試,已經(jīng)無法滿足如今的業(yè)務(wù)要求,只能被企業(yè)逐步裁員淘汰。 「自動(dòng)化測(cè)試

    2024年02月10日
    瀏覽(27)
  • Python web自動(dòng)化測(cè)試框架搭建(功能&接口)——通用模塊

    Python web自動(dòng)化測(cè)試框架搭建(功能&接口)——通用模塊

    1、通用模塊: config.conf: 公共配置文件,配置報(bào)告、日志、截圖路徑,以及郵件相關(guān)配置 logger: 日志模塊 main.py: 執(zhí)行器,負(fù)責(zé)執(zhí)行整體測(cè)試任務(wù)模塊 testrunner.py: 負(fù)責(zé)測(cè)試用例執(zhí)行和結(jié)果收集 utils.py: 公共方法,如創(chuàng)建報(bào)告文件夾、生成測(cè)試報(bào)告、發(fā)送郵件 2、日志模塊: 3、

    2024年01月16日
    瀏覽(22)
  • 【實(shí)戰(zhàn)詳解】如何快速搭建接口自動(dòng)化測(cè)試框架?Python + Requests

    【實(shí)戰(zhàn)詳解】如何快速搭建接口自動(dòng)化測(cè)試框架?Python + Requests

    本文主要介紹如何使用Python語言和Requests庫(kù)進(jìn)行接口自動(dòng)化測(cè)試,并提供詳細(xì)的代碼示例和操作步驟。希望能對(duì)讀者有所啟發(fā)和幫助。 隨著移動(dòng)互聯(lián)網(wǎng)的快速發(fā)展,越來越多的應(yīng)用程序采用Web API(也稱為RESTful API)作為數(shù)據(jù)交換的主要方式。針對(duì)API進(jìn)行自動(dòng)化測(cè)試已經(jīng)變得非

    2024年02月09日
    瀏覽(24)
  • Python + Selenium,分分鐘搭建 Web 自動(dòng)化測(cè)試框架!

    Python + Selenium,分分鐘搭建 Web 自動(dòng)化測(cè)試框架!

    在程序員的世界中,一切重復(fù)性的工作,都應(yīng)該通過程序自動(dòng)執(zhí)行。 「自動(dòng)化測(cè)試」就是一個(gè)最好的例子。 隨著互聯(lián)網(wǎng)應(yīng)用開發(fā)周期越來越短,迭代速度越來越快,只會(huì)點(diǎn)點(diǎn)點(diǎn),不懂開發(fā)的手工測(cè)試,已經(jīng)無法滿足如今的業(yè)務(wù)要求,只能被企業(yè)逐步裁員淘汰。 「自動(dòng)化測(cè)試

    2024年02月02日
    瀏覽(29)
  • web自動(dòng)化測(cè)試框架落地實(shí)施全過程-測(cè)試環(huán)境搭建 (Selenium+Python)

    web自動(dòng)化測(cè)試框架落地實(shí)施全過程-測(cè)試環(huán)境搭建 (Selenium+Python)

    Web自動(dòng)化測(cè)試是指使用自動(dòng)化工具模擬用戶在Web瀏覽器中執(zhí)行的操作,通過編寫腳本來自動(dòng)化執(zhí)行測(cè)試用例,以驗(yàn)證Web應(yīng)用程序的功能、性能和兼容性等方面的質(zhì)量。其主要目的是降低測(cè)試成本和時(shí)間,并提高測(cè)試效率和準(zhǔn)確性。 Web自動(dòng)化測(cè)試通常包括以下步驟: 確定測(cè)試

    2024年02月09日
    瀏覽(20)
  • (Python)Requests+Pytest+Allure接口自動(dòng)化測(cè)試框架從0到1搭建

    (Python)Requests+Pytest+Allure接口自動(dòng)化測(cè)試框架從0到1搭建

    前面,已經(jīng)學(xué)習(xí)了如何用SpringBoot寫接口以及與Mysql數(shù)據(jù)庫(kù)進(jìn)行交互,具體可查閱下面的這篇博客,今天學(xué)習(xí)一下基于Python的接口自動(dòng)化測(cè)試框架的搭建,主要包括以下內(nèi)容:利用request庫(kù)發(fā)送請(qǐng)求,請(qǐng)求數(shù)據(jù)參數(shù)化處理,還涉及到數(shù)據(jù)庫(kù)(Mysql+MongDB)方面的交互,包括如何取數(shù)

    2024年02月13日
    瀏覽(722)
  • Python 自動(dòng)化測(cè)試框架環(huán)境怎么搭建?這篇文章給你講的明明白白

    Python 自動(dòng)化測(cè)試框架環(huán)境怎么搭建?這篇文章給你講的明明白白

    目錄 Python 自動(dòng)化測(cè)試框架環(huán)境搭建 第一步:安裝 Python 第二步:安裝 PyCharm 第三步:安裝 Selenium WebDriver 第四步:安裝瀏覽器驅(qū)動(dòng) 第五步:創(chuàng)建測(cè)試用例 第六步:集成持續(xù)集成平臺(tái) 總結(jié) Python 是一種流行的編程語言,可以用于多種應(yīng)用場(chǎng)景,包括自動(dòng)化測(cè)試。本文將介紹如

    2023年04月12日
    瀏覽(69)
  • 接口自動(dòng)化測(cè)試框架搭建【附詳細(xì)搭建視頻】

    接口自動(dòng)化測(cè)試框架搭建【附詳細(xì)搭建視頻】

    如果遇到什么問題建議觀看下面視頻: 【敢稱全站第一】B站最全的Python自動(dòng)化測(cè)試深度學(xué)習(xí)教程!學(xué)完即就業(yè),小白也能信手拈來!幫你少走99%的彎路~ 一、原理及特點(diǎn) 參數(shù)放在XML文件中進(jìn)行管理 用httpClient簡(jiǎn)單封裝一個(gè)httpUtils工具類 測(cè)試用例管理使用了testNg管理,使用了

    2024年02月07日
    瀏覽(48)
  • 如何搭建自動(dòng)化測(cè)試框架?資深測(cè)試整理的PO模式,一套打通自動(dòng)化...

    如何搭建自動(dòng)化測(cè)試框架?資深測(cè)試整理的PO模式,一套打通自動(dòng)化...

    Po模型介紹 1、簡(jiǎn)介 在自動(dòng)化中,Selenium自動(dòng)化測(cè)試中有一個(gè)名字經(jīng)常被提及PageObject(思想與面向?qū)ο蟮奶卣飨嗤?,通常PO模型可以大大提高測(cè)試用例的維護(hù)效率 2、為什么要用PO 基于selenium2開始ui自動(dòng)化測(cè)試腳本的編寫不是多么艱巨的任務(wù)。只需要定位到元素,執(zhí)行對(duì)應(yīng)元素的

    2024年02月13日
    瀏覽(97)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包