自己給網(wǎng)站做優(yōu)化怎么做百度seo高級優(yōu)化
🍅 視頻學(xué)習(xí):文末有免費的配套視頻可觀看
🍅?關(guān)注公眾號:互聯(lián)網(wǎng)雜貨鋪,回復(fù)1?,免費獲取軟件測試全套資料,資料在手,漲薪更快
接口測試三要素:
-
參數(shù)構(gòu)造
-
發(fā)起請求,獲取響應(yīng)
-
校驗結(jié)果
一、原始狀態(tài)
當(dāng)我們的用例沒有進行分層設(shè)計的時候,只能算是一個“苗條式”的腳本。以一個后臺創(chuàng)建商品活動的場景為例,大概流程是這樣的(默認已經(jīng)是登錄狀態(tài)下):
創(chuàng)建商品-創(chuàng)建分類-創(chuàng)建優(yōu)惠券-創(chuàng)建活動
要進行接口測試的話,按照接口測試的三要素來進行,具體的效果如下:
# 1、參數(shù)構(gòu)造createCommodityParams = {"input": {"title": "活動商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "顏色","attrValue": "綠色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)createCategoryParams["input"]["categoryName"] = "autoTestCategory" + str(time.time())createCouponParams。。。createPublicityParams。。。publishCommodityParams。。。publishPublicityParams。。。# 2、發(fā)起請求,獲取響應(yīng)# 創(chuàng)建商品并獲取商品codecreateCommodityRes = api.getUrl("testApi.create.commodity").post.params(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 創(chuàng)建分類并獲取分類codecreateCategoryRes = api.getUrl("testApi.create.category").post.params(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 創(chuàng)建優(yōu)惠券并獲取優(yōu)惠券codecreateCouponRes = api.getUrl("testApi.create.coupon").post.params(createCouponParams)couponCode = createCouponRes["couponCode"]# 創(chuàng)建活動并關(guān)聯(lián)商品,綁定優(yōu)惠券,設(shè)置分類createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = api.getUrl("testApi.create.publicity").post.params(createPublicityParams)# 結(jié)果校驗(斷言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
按照上面的寫法,對于單個腳本的調(diào)式來說或許可以,但是一旦用例的數(shù)量和復(fù)雜程度積累起來后,其維護成本將是巨大的,或者可以說不具備可維護性。
弊端說明:
-
可讀性差,所有的處理都放在一起,代碼量大,不簡潔直觀
-
靈活性差,參數(shù)寫死在腳本,適用用例范圍小
-
復(fù)用性差,如果其他用例需要同樣或類似的步驟,需要重新寫一份
-
維護性差,如果接口有任何改動,那么所有涉及到此接口的腳本都需要一一修改
例如:隨著用例場景的增加,就可能會出現(xiàn)下面這種情況
按照原始的模式,我們就需要些3個腳本文件分別來描述著3個場景,并且創(chuàng)建商品_API
、創(chuàng)建分類_API
、創(chuàng)建優(yōu)惠券_API
在場景1,2,3中均出現(xiàn)了;上架商品_API
在場景2,3中均出現(xiàn)。由此我們完全可以預(yù)見到,當(dāng)幾百上千的用例場景出現(xiàn)后,這種形式是沒有維護性可言的。
二、進化歷程
因此我們依照著痛點,以最開始的原始狀態(tài)為例,對用例進行分層改造,來看看進化后的狀態(tài)。
1、API 定義層
我們編程的時候會將一些重復(fù)的代碼進行封裝使用,那么這里依然可以借用這種思想,我們將 API 的定義單獨抽離,單獨定義。
我們期望的效果是這樣的:
提前將API的定義放在一層,供用例場景引用,這樣當(dāng)接口有任何修改時,我們只需要修改API definition
層即可。
實例演示
對應(yīng)著上面的demo,我們就是需要做如下抽離:
class APIDefinition:
‘’’
創(chuàng)建商品API定義
createCommodityParams: 創(chuàng)建商品接口入?yún)?br /> return:創(chuàng)建商品接口響應(yīng)結(jié)果
‘’’
def createCommodityRequest(createCommodityParams):
return api.getUrl(“testApi.create.commodity”).post.params(createCommodityParams)
'''創(chuàng)建分類API定義createCategoryParams: 創(chuàng)建分類接口入?yún)eturn:創(chuàng)建分類接口響應(yīng)結(jié)果''' def createCategoryRequest(createCategoryParams)return api.getUrl("testApi.create.category").post.params(createCategoryParams)# 創(chuàng)建優(yōu)惠券接口定義def createCouponRequest(createCouponParams)return api.getUrl("testApi.create.coupon").post.params(createCouponParams)# 創(chuàng)建活動接口定義def createPublicityRequest(createPublicityParams)return api.getUrl("testApi.create.publicity").post.params(createPublicityParams)# ...其余省略
2、Service 層
上面我們已經(jīng)將接口的定義抽離出來,解決了 API 重復(fù)定義的問題,但是再繼續(xù)分析會發(fā)現(xiàn)有一個問題依然沒有解決,就是場景的復(fù)用性.
再看剛才的圖:
3個場景中都有重復(fù)的步驟,類似創(chuàng)建商品
、創(chuàng)建分類
、創(chuàng)建優(yōu)惠
券這些,并且這些步驟都是一個個API的組合,一個步驟對應(yīng)一個API,在各個步驟之間還會有數(shù)據(jù)的處理與傳遞,為了解決這些問題,將對場景再次做抽離,這里我稱之為?service
?層。
這一層之所以叫做
service(服務(wù))
層,是因為它的作用是用來提供測試用例所需要的各種“服務(wù)”,好比參數(shù)構(gòu)建、接口請求、數(shù)據(jù)處理、測試步驟。
用下圖先來看分層的目標(biāo):
我們希望將常用的測試場景步驟封裝至service
層中,供用例場景調(diào)用,增加復(fù)用性,也可以理解為測試用例的前置處理;
但是這里還是有一點小問題,就是service
層的東西太多太雜,有些場景步驟可能只適用于我當(dāng)前的項目用例,在實際的工作中,各個系統(tǒng)間是相互依賴的,前臺APP的測試很大可能就依賴后臺創(chuàng)建作為前置條件
好比我在APP端只要商品和分類,可能只想創(chuàng)建商品和分類,并不想創(chuàng)建優(yōu)惠券,這個時候service層就沒有適用的場景步驟供調(diào)用,那么我就需要根據(jù)自己的需要重新封裝;可是對于很多單接口的前置數(shù)據(jù)處理又是一致的,比如:
createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)createCategoryParams["input"]["categoryName"] = "autoTestCategory" + str(time.time())createCouponParams。。。createPublicityParams。。。publishCommodityParams。。。publishPublicityParams。。。
重新封裝的話還要再處理這一步,就有點麻煩且不符合我們的復(fù)用性設(shè)計了,因此我們對service
層再細化為3層,分別為:
apiObject:
單接口的預(yù)處理層,這一層主要作用是單接口入?yún)⒌臉?gòu)造,接口的請求與響應(yīng)值返回
-
每個接口請求不依賴與業(yè)務(wù)步驟,都是單接口的請求;
-
此外一些簡單固定的入?yún)?gòu)建也直接放在這里處理,比如隨機的商品名,title等,和具體業(yè)務(wù)流程無關(guān),針對所有調(diào)用此接口的場景均適用
caseService:
多接口的預(yù)處理層,這一層主要是
測試步驟(teststep)
或場景的有序集合。
-
用例所需要的步驟,通過每一個請求進行組合,每一個步驟都對應(yīng)著一個API請求,這些步驟會組成一個個場景,各個場景之間可以互相調(diào)用組成新的場景,以適應(yīng)不同的測試用例需求。
-
場景封裝好以后可以供不同的測試用例調(diào)用,除了當(dāng)前項目的用例,其他業(yè)務(wù)線需要的話也可從此
caseService
中選擇調(diào)用,提高復(fù)用性的同時也避免了用例相互依賴的問題。
util:
這一層主要放置針對當(dāng)前業(yè)務(wù)的接口需要處理的數(shù)據(jù)
- 在實際編寫測試步驟時,可能部分接口的參數(shù)是通過其他接口獲取后經(jīng)過處理才可以使用,或是修改數(shù)據(jù)格式,或是修改字段名稱,亦或是某些 value 的加解密處理等。
細化分層后,各層的職責(zé)便更加清晰明確,具體如下圖:
實例演示
apiObject:
class ApiObject:def createCommodity(createCommodityParams):inputParams = ApiParamsBuild().createCommodityParamsBuild(createCommodityParams)response = APIDefinition().createCommodityRequest(inputParams)return responsedef createCategory(createCategoryParams):...def createCoupon(createCouponParams):.........class ApiParamsBuild:def createCommodityParamsBuild(createCommodityParams):createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)return createCommodityParamsdef createCategoryParamsBuild(createCategoryParams):...def createCouponParamsBuild(createCouponParams):.........
到此,我們來看看原始的用例經(jīng)過目前封裝后的模樣:
1、參數(shù)構(gòu)造
createCommodityParams = {"input": {"title": "活動商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "顏色","attrValue": "綠色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}# 2、發(fā)起請求,獲取響應(yīng)# 創(chuàng)建商品并獲取商品codecreateCommodityRes = ApiObject().createCommodity(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 創(chuàng)建分類并獲取分類codecreateCategoryRes = ApiObject().createCategory(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 創(chuàng)建優(yōu)惠券并獲取優(yōu)惠券codecreateCouponRes = ApiObject().createCoupon(createCouponParams)couponCode = createCouponRes["couponCode"]# 創(chuàng)建活動并關(guān)聯(lián)商品,綁定優(yōu)惠券,設(shè)置分類createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = ApiObject().createPublicity(createPublicityParams)# 結(jié)果校驗(斷言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
可以看到,現(xiàn)在接口請求的url、method、通用入?yún)⑻幚淼纫呀?jīng)不會在用例中體現(xiàn)了,接下來繼續(xù)封裝caseService層。caseService:我們將多接口的場景步驟進行封裝
``` class CaseService:def createPublicityByCategory(params):# 創(chuàng)建商品并獲取商品codecreateCommodityRes = ApiObject().createCommodity(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 創(chuàng)建分類并獲取分類codecreateCategoryRes = ApiObject().createCategory(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 創(chuàng)建優(yōu)惠券并獲取優(yōu)惠券codecreateCouponRes = ApiObject().createCoupon(createCouponParams)couponCode = createCouponRes["couponCode"]# 創(chuàng)建活動并關(guān)聯(lián)商品,綁定優(yōu)惠券,設(shè)置分類createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = ApiObject().createPublicity(createPublicityParams)return createPublicityRes......
這時體現(xiàn)在用例中的表現(xiàn)就如下層testcase層所示.
3、testcase 層
我們想要的是一個清晰明了,“一勞永逸”的自動化測試用例,就像我們的手工測試用例一樣,我們的前置條件可以復(fù)用,我們?nèi)雲(yún)⒖梢匀我庑薷?#xff0c;但測試步驟都是固定不變的(前提可能是產(chǎn)品沒有偷偷改需求~)。
這一層其實是對應(yīng)的testsuite(測試用例集),是測試用例的無序集合。其中各個用例之間應(yīng)該是相互獨立,互不干擾,不存在依賴關(guān)系,每個用例都可以單獨運行。
最終我們期望自動化用例的維護過程中達到的效果如下:
testcase 層:
# 1、參數(shù)構(gòu)造createCommodityParams = {"input": {"title": "活動商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "顏色","attrValue": "綠色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}# 2、發(fā)起請求,獲取響應(yīng)createPublicityRes = CaseService().createPublicityByCategory(createCommodityParams,createCategoryParams,createCouponParams...)# 結(jié)果校驗(斷言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
可以看到,這時涉及到用例場景步驟的代碼已經(jīng)非常少了,并且完全獨立,與框架、其他用例等均無耦合。
到這里我們再看用例,會發(fā)現(xiàn)一點,測試數(shù)據(jù)依然冗長,那么下面就開始對測試數(shù)據(jù)進行參數(shù)化和數(shù)據(jù)驅(qū)動的處理。
4、testdata
此層用來管理測試數(shù)據(jù),作為參數(shù)化場景的數(shù)據(jù)驅(qū)動。
參數(shù)化: 所謂參數(shù)化,簡單來說就是將入?yún)⒗米兞康男问絺魅?#xff0c;不要將參數(shù)寫死,增加靈活性,好比搜索商品的接口,不同的關(guān)鍵字和搜索范圍作為入?yún)?#xff0c;就會得到不同的搜索結(jié)果。上面的例子中其實已經(jīng)是參數(shù)化了。
數(shù)據(jù)驅(qū)動:對于參數(shù),我們可以將其放入一個文件中,可以存放多個入?yún)?#xff0c;形成一個參數(shù)列表的形式,然后從中讀取參數(shù)傳入接口即可。常見做數(shù)據(jù)驅(qū)動的有 JSON、CSV、YAML 等。
實例演示
我們以CSV為例,不特別依照某個框架,通常測試框架都具備參數(shù)化的功能。
將所需要的入?yún)⒎湃雝est.csv文件中:
createCommodityParams,createCategoryParams,...{"input": {"title": "活動商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "顏色","attrValue": "綠色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}},...
然后再回到用例層,利用框架參數(shù)化的功能對數(shù)據(jù)進行讀取
# 1、參數(shù)構(gòu)造@parametrize(params = readCsv("test.csv"))# 2、發(fā)起請求,獲取響應(yīng)createPublicityRes = CaseService().createPublicityByCategory(params)# 結(jié)果校驗(斷言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
注:這里的測試數(shù)據(jù),不僅僅局限于接口的請求參數(shù),既然做數(shù)據(jù)驅(qū)動,那么斷言也可以維護在此,以減少用例層的代碼冗余。
5、rawData
這一層是存放接口原始入?yún)⒌牡胤健?/p>
某些接口的入?yún)⒖赡芎芏?#xff0c;其中很多參數(shù)值又可能是固定不變的,構(gòu)建入?yún)⒌臅r候我們只想對"變"的值進行動態(tài)的維護,而不維護的值就使用原始參數(shù)中的默認值,以此減少工作量(emmm…可能也就是CV大法的量吧~)
再者就是數(shù)據(jù)驅(qū)動的數(shù)據(jù)文件中只維護需要修改的參數(shù),使數(shù)據(jù)文件更簡潔,可閱讀性更強。
實例演示:
這種利用原始參數(shù)(rawData)的方法我們稱之為模板化,實際工作中有多種方式可實現(xiàn),例如jsonpath、Mustache或者自己根據(jù)需求實現(xiàn)方法,本文重點在介紹分層設(shè)計,所以就不具體演示模板化技術(shù)的細節(jié)了,僅說明設(shè)計此層的作用。
以實例中的入?yún)reateCommodityParams為例,未用模板化技術(shù)前,我們要在CSV里面維護完整的入?yún)?#xff1a;
createCommodityParams,createCategoryParams,...{"input": {"title": "活動商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "顏色","attrValue": "綠色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}},...
但是實際上,我們可能僅僅需要修改維護其中某個或某幾個字段(例如只想維護商品價格),其余的使用默認值即可,使用模板化技術(shù)后可能在CSV中就是這樣的表現(xiàn):
createCommodityParams,createCategoryParams,...{"input": {"skuList": [{"price": 1,"retailPrice": 6}},...
或者這樣
- keyPath: $.input.skuList[0].pricevalue: 1- keyPath: $.input.skuList[0].retailPricevalue: 6
亦或使用Mustache,將需要修改的value進行參數(shù)化{{value}}。
我們可以看到,這樣處理后的數(shù)據(jù)驅(qū)動的文件就變得簡潔清晰的許多,當(dāng)一個文件中維護了多個用例且入?yún)⒆侄魏芏鄷r,這樣維護起來就可以清晰的看出每個數(shù)據(jù)對應(yīng)的用例的作用了;
price就是為了測試價格的,stock就是為了測試庫存的,publish就是為了測試上下架的等等。
注: 當(dāng)然,此層的使用視實際情況而定,有可能這個接口的參數(shù)本身就沒多少,那么直接全量使用就行,或者你就是覺得數(shù)據(jù)量哪怕再大我都能分得清楚,看的明白,不用也rawData是可以的~
6、Base
此層主要放置我們需要處理的公共前置條件和一些自動化公共方法,也可以理解為公共的config和util。
在我們實際的自動化開發(fā)過程中,有很多前置條件或公共方法,比如登錄處理,log 處理,斷言方法或一些數(shù)據(jù)處理;
使用過程中所有的service和testcase層都會繼承此類,這樣這些公共方法和前置條件便可直接通用;在各個業(yè)務(wù)線之間也可保持一致性。
三、完結(jié)
最后,我們來看下整體分層后的目錄結(jié)構(gòu)總覽:
└─apiautotest└─project└─rawData(原始參數(shù))├─testRawData.json└─service(用例服務(wù))└─apiObject(單接口預(yù)處理,單接口入?yún)⒌臉?gòu)造,接口的請求與響應(yīng)值返回)├─testApiObject.py└─caseService(多接口預(yù)處理,測試步驟(teststep)或場景的有序集合)├─testCaseService.py└─util(工具類)├─util.py└─testcase(測試用例)└─testDataDriven(測試數(shù)據(jù)驅(qū)動)├─testData.csv├─testcase.py(測試用例集)└─testBase.py(測試基類,初始化和公共方法) └─platformapi(Api定義)├─testApiDefinition.py
同時,在這我為大家準(zhǔn)備了一份軟件測試視頻教程(含面試、接口、自動化、性能測試等),就在下方,需要的可以直接去觀看,也可以直接【點擊文末小卡片免費領(lǐng)取資料文檔】
自動化測試從菜鳥到高手【2024最新完整版】,從基礎(chǔ)到項目實戰(zhàn)(Python自動化測試保姆級教程)