升職加薪之 HttpRunner 請求 Dubbo 介面

大佬喝可樂發表於2020-07-20

國內大部分公司目前都是使用基於Java語言的 Dubbo技術棧,而測試同事普遍對Python技術棧更為熟悉。為了使不懂JAVA程式碼的測試同事也能進行Dubbo介面層的測試,故對HttpRunner進行二次開發,新增對Dubbo介面的支援

1、實現原理
關於HttpRunner我想不用多做介紹,測試小夥伴應該都瞭解,這是一款非常優秀的面向 HTTP(S) 協議的通用測試框架,我們要做的是基於這個框架進行二次開發。

根據Dubbo官方文件中提到的:dubbo可以通過telnet命令進行服務治理,詳情見​​Dubbo官方文件

而在Python中有一個第三方包 telnetlib,所以我們可以通過這個包來執行telnet命令,進而對dubbo介面進行呼叫

通過上圖我們還了解到一個資訊點,那就是如果我要通過telnet連線伺服器,我需要 ip 還有 埠號。下面,讓我們一步步來實現

2、Dubbo服務圖解
​​
​​

我們可以通過Dubbo服務的架構圖瞭解到,要獲取Dubbo服務,我們需要去zookeeper服務註冊中心找到對應的服務即可

3、telnet連線Dubbo服務
使用Docker容器部署微服務,每次重啟服務ip都會發生變化(原因:容器間則不用ip直接通訊,而使用主機名、復服務名、網路別名),所以在使用telnet連線時需要獲取動態的ip,我司專案目前使用的dubbo-monitor進行管理

通過爬蟲的思想,可動態獲取服務 ip和埠號,連線上伺服器之後,我們就可以通過模擬命令列對服務進行治理

4、如何使用
4.1、請求服務相關程式碼

這段程式碼是處理請求的程式碼,放在httprunner原始碼檔案下的utils資料夾內

import time
import os
import sys
import telnetlib


class TelnetClient(object):
"""通過telnet連線dubbo服務, 執行shell命令, 可用來呼叫dubbo介面
"""


def __init__(self, server_host, server_port):
self.tn = telnetlib.Telnet()
self.server_host = server_host
self.server_port = server_port

# 此函式實現telnet登入主機
def connect_dubbo(self):
try:
print("telent連線dubbo服務端: telnet {} {} ……".format(self.server_host, self.server_port))
self.tn.open(self.server_host, port=self.server_port)
return True
except Exception as e:
print('連線失敗, 原因是: {}'.format(str(e)))
return False

# 執行傳過來的命令,並輸出其執行結果
def execute_some_command(self, command):
# 執行命令
cmd = (command + '\n').encode("gbk")
self.tn.write(cmd)

# 獲取命令結果,字串型別
retry_count = 0
# 如果響應未及時返回,則等待後重新讀取,並記錄重試次數
result = self.tn.read_very_eager().decode(encoding='gbk')
while result == '':
time.sleep(1)
result = self.tn.read_very_eager().decode(encoding='gbk')
retry_count += 1
return result

# 退出telnet
def logout_host(self):
self.tn.write(b"exit\n")
print("登出成功")


class InvokeDubboApi(object):

def __init__(self, server_host, server_port):
try:
self.telnet_client = TelnetClient(server_host, server_port)
self.login_flag = self.telnet_client.connect_dubbo()
except Exception as e:
print("invokedubboapi init error" + str(e))



def invoke_dubbo_api(self, dubbo_service, dubbor_method, *args):
api_name = dubbo_service + "." + dubbor_method + "{}"
cmd = "invoke " + api_name.format(args)
print("呼叫命令是:{}".format(cmd))
resp0 = None
try:
if self.login_flag:
resp0 = self.telnet_client.execute_some_command(cmd)
print("介面響應是,resp={}".format(resp0))
# dubbo介面返回的資料中有 elapsed: 4 ms. 耗時,需要使用elapsed 進行切割
return str(re.compile(".+").findall(resp0).pop(0)).split("elapsed").pop(0).strip()
else:
print("登陸失敗!")
except Exception as e:
raise Exception("呼叫介面異常, 介面響應是resp={}, 異常資訊為:{}".format(resp0, str(e)))
self.logout()

def logout(self):
self.telnet_client.logout_host()

class GetDubboService2(object):
def __init__(self):
pass

def get_dubbo_info2(self,content):
try:
dubbore = re.compile(r"([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)", re.I)
result = dubbore.search(str(content)).group()
print("獲取到dubbo部署資訊" + result)
return {"server_host": result.split(":")[0], "server_port": result.split(":")[1]}
except Exception as e:
raise Exception("獲取dubbo部署資訊失敗:{}".format(str(e)))

這段程式碼放在 functions中,這樣在yml檔案中可以直接通過 ${方法名}進行呼叫

​​

程式碼如下:

def invoke_dubbo2(content, dubbo_service, dubbo_method, *args):
'''
通過 zk管理後臺查詢dubbo註冊資訊
:param dubbo_service: dubbo 服務名 如:com.zl.mall.api.IItemService
:param dubbor_method: 服務中的方法 如:updateItem
:param args: 方法請求需要的引數
:return:
'''
dubbo_info = GetDubboService2().get_dubbo_info2(content)
invokeDubboApi = InvokeDubboApi(server_host=dubbo_info.get("server_host"),
server_port=dubbo_info.get("server_port"))
return invokeDubboApi.invoke_dubbo_api(dubbo_service, dubbo_method, *args)

將程式碼新增至框架之後,即可在yml檔案中使用

4.2、新增testcase

和在專案中填寫Http testcase類似,其中新增的幾個variables引數為

dubbo_service: com.zl.item.api.IItemService ---Dubbo服務名稱 (必填)
dubbo_method: queryItemByLstItemId ---Dubbo服務方法名(必填)
iItemIdList: [123,12323] ---方法的請求引數(根據Dubbo定義的方法來調整入參)

${invoke_dubbo2()}    請求dubbo介面的方法,按照對應的格式填寫即可請求DUbbo介面

4.3、執行testcase

執行testcase,檢視控制檯資訊,其中resp 即為 dubbo介面返回的資訊,對比validate中設定的預期值,即可完成對Dubbo介面的測試

5、注意事項
5.0、請求引數異常

 請求Dubbo介面如果填入的引數有誤,會報 no such method 的錯誤,請檢查一下引數是否正常

5.1、列舉類請求:

列舉類的類名:  com.zl.item.entity.StudentEnum 

需要使用到的列舉類:GOOD_STUDENT,填寫格式如下 :

{"name": "GOOD_STUDENT", "class": "com.zl.item.entity.StudentEnum "}

5.2、實體類請求:

實體類類名:com.zl.item.entity.Student

實體類的欄位: ,填寫格式如下:  "Id":123,"code":"abc"

{"class":"com.zl.item.entity.Student","Id":123,"code":"abc"}

5.3、對於請求方法是void方法的校驗設定

java程式碼中void這種方法是沒有返回值的,所以我們直接用“null”。如果需要進一步校驗資料的準確性,可以校驗這個方法改變的特性

5.4、對於入參是Boolean型別的資料

在json中,直接 使用{“data”:true}即可,但是在python請求時需要使用 “true”,加上雙引號

相關文章