Python知識分享網(wǎng) - 專業(yè)的Python學(xué)習(xí)網(wǎng)站 學(xué)Python,上Python222
【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證
發(fā)布于:2023-07-18 14:02:15

測試工作中常用到的測試樁mock能力

在我們的測試工作過程中,可能會遇到前端服務(wù)開發(fā)完成,依賴服務(wù)還在開發(fā)中;或者我們需要壓測某個服務(wù),而這個服務(wù)的依賴組件(如測試環(huán)境MQ) 無法支撐并發(fā)訪問的場景。這個時候我們可能就需要一個服務(wù),來替代測試環(huán)境的這些依賴組件或服務(wù),而這就是本文的主角--測試樁。

測試樁可以理解為一個代理,它可以用于模擬應(yīng)用程序中的外部依賴項,如數(shù)據(jù)庫、網(wǎng)絡(luò)服務(wù)或其他API,它可以幫助我們在開發(fā)和測試過程中隔離應(yīng)用程序的不同部分,從而使測試更加可靠和可重復(fù)。

 

應(yīng)用場景

測試樁使用的一般有以下幾種場景:

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖1

本文將選取常用的幾個場景循序漸進地介紹測試樁的開發(fā)和優(yōu)化。

 

簡單測試樁

如果在測試環(huán)境中不方便安裝其他的庫,我們可以使用Python標準庫中的一個模塊http.server模塊創(chuàng)建一個簡單的HTTP請求測試樁。

 

# simple_stub.py
# 測試樁接收GET請求并返回JSON數(shù)據(jù)。
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        content = json.dumps({"message": "Hello, this is a test stub!"}).encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", f"{len(content)}")
        self.end_headers()
        self.wfile.write(content)


if __name__ == "__main__":
    server_address = ("", 8000)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    print("Test stub is running on port 8000")
    httpd.serve_forever()

 

運行上面的代碼,將看到測試樁正在監(jiān)聽8000端口。您可以使用瀏覽器或curl命令訪問 http://localhost:8000,將會收到 {'message': 'Hello, this is a test stub!'}的響應(yīng)。

 

http.server擴展:一行命令實現(xiàn)一個靜態(tài)文件服務(wù)器

http.server模塊可以作為一個簡單的靜態(tài)文件服務(wù)器,用于在本地開發(fā)和測試靜態(tài)網(wǎng)站。要啟動靜態(tài)文件服務(wù)器,請在命令行中運行以下命令:

 

python3 -m http.server [port]

 

其中[port]是可選的端口號,不傳遞時默認為8000。服務(wù)器將在當前目錄中提供靜態(tài)文件。

如在日志文件夾中執(zhí)行python -m http.server,就能在web瀏覽器中訪問這個文件夾中的文件和子文件夾的內(nèi)容:

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖2

注意: http.server主要用于開發(fā)和測試,性能和安全方面不具備在生產(chǎn)環(huán)境部署的條件

 

性能優(yōu)化:使用異步響應(yīng)

我們在前面的實現(xiàn)了一個簡單的測試樁,但在實際應(yīng)用中,我們可能需要更高的性能和更復(fù)雜的功能。

 
異步響應(yīng)

在只有同樣的資源的情況下,像這樣的有網(wǎng)絡(luò)I/O的服務(wù),使用異步的方式無疑能更有效地利用系統(tǒng)資源。

說到異步的http框架,目前最火熱的當然是FastAPI,使用FastAPI實現(xiàn)上面的功能只需兩步。

首先,安裝FastAPI和Uvicorn:

 

pip install fastapi uvicorn

 

接下來,創(chuàng)建一個名為fastapi_stub.py的文件,其中包含以下內(nèi)容:

 

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def get_request():
    return {"message": "Hello, this is an optimized test stub!"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

 

執(zhí)行代碼,這個測試樁也是監(jiān)聽在8000端口。我們可以像之前那樣使用瀏覽器或其他HTTP客戶端向測試樁發(fā)起請求。

 

異步編程優(yōu)勢 介紹

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖3

需要注意的是,雖然異步編程在許多場景下可以提供更好的性能,但它并不總是比同步編程快。在某些情況下,如CPU密集型任務(wù),異步編程可能無法帶來明顯的性能提升。此外,異步編程通常需要更復(fù)雜的編程模型和錯誤處理機制,因此在選擇異步編程時需要權(quán)衡其優(yōu)缺點。

 

性能優(yōu)化:利用多核

雖然我們前面使用到了異步的方式來提升測試樁的性能,但是代碼還是只是跑在一個CPU核心上,如果我們要進行性能壓測,可能無法滿足我們的性能需求。這個時候我們可以使用 gunicorn庫 來利用上服務(wù)器的多核優(yōu)勢。

 

gunicorn

Gunicorn的主要特點和優(yōu)勢:

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖4

 
安裝 gunicorn

 

pip install gunicorn

 

使用 gunicorn 啟動服務(wù)

啟動服務(wù):

 

gunicorn -w 4  fastapi_stub:app 

 

可以看到,上面的命令啟動了4個worker 進程,大家也可以使用ps -ef命令查詢一下進程狀態(tài)。

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖5

gunicorn的一些常用參數(shù):

【Python】從同步到異步多核:測試樁性能優(yōu)化,加速應(yīng)用的開發(fā)和驗證 圖6

 

Gunicorn提供了許多其他配置選項,可以根據(jù)具體需求進行調(diào)整。要查看完整的選項列表,可以查看Gunicorn的官方文檔:https://docs.gunicorn.org/en/stable/settings.html。

 

性能優(yōu)化:使用緩存(functools.lru_cache)。

當處理重復(fù)的計算或數(shù)據(jù)檢索任務(wù)時。使用內(nèi)存緩存(如Python的functools.lru_cache)或外部緩存(如Redis)來緩存經(jīng)常使用的數(shù)據(jù)也能極大的提升測試樁的效率。

假設(shè)我們的測試樁需要使用到計算計算斐波那契數(shù)列這樣耗時的功能,那么緩存結(jié)果,在下次遇到同樣的請求時直接返回而不是先計算再返回,將極大的提高資源的使用率、減少響應(yīng)的等待時間。

如果僅僅是直接返回數(shù)據(jù)的,沒有進行復(fù)雜的計算的測試樁,使用lru_cache并沒有實際意義。

以下是一個更合適的使用lru_cache的示例,其中我們將對斐波那契數(shù)列進行計算并緩存結(jié)果:

 

from fastapi import FastAPI
from functools import lru_cache

app = FastAPI()

@lru_cache(maxsize=100)
def fibonacci(n: int):
    if n <= 1:
        return n
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

@app.get("/fibonacci/{n}")
async def get_fibonacci(n: int):
    result = fibonacci(n)
    return {"result": result}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

 

在這個示例中,我們使用FastAPI創(chuàng)建了一個簡單的HTTP請求測試樁。我們定義了一個名為fibonacci的函數(shù),該函數(shù)計算斐波那契數(shù)列。為了提高性能,我們使用functools.lru_cache對該函數(shù)進行了緩存。

在路由/fibonacci/{n}中,我們調(diào)用fibonacci函數(shù)并返回結(jié)果??梢悦钤L問 http://localhost:8000/fibonacci/{n}進行調(diào)試。

需要注意的是 maxsize參數(shù)是functools.lru_cache裝飾器的一個配置選項,它表示緩存的最大容量。lru_cache使用字典來存儲緩存項,當一個新的結(jié)果需要被緩存時,它會檢查當前緩存的大小。如果緩存已滿(即達到maxsize),則會根據(jù)LRU策略移除最近最少使用的緩存項。如果maxsize設(shè)置為None,則緩存可以無限制地增長,這可能導(dǎo)致內(nèi)存問題。

 

 

單元測試中的mock

 

Python unittest.mock

在Python中,unittest模塊提供了一個名為unittest.mock的子模塊,用于創(chuàng)建mock對象。unittest.mock包含一個名為Mock的類以及一個名為patch的上下文管理器/裝飾器,可以用于替換被測試代碼中的依賴項。

 

import requests
from unittest import TestCase
from unittest.mock import patch

# 定義一個函數(shù) get_user_name,它使用 requests.get 發(fā)起 HTTP 請求以獲取用戶名稱
def get_user_name(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()["name"]

# 創(chuàng)建一個名為 TestGetUserName 的測試類,它繼承自 unittest.TestCase
class TestGetUserName(TestCase):
    # 使用 unittest.mock.patch 裝飾器替換 requests.get 函數(shù)
    @patch("requests.get")
    # 定義一個名為 test_get_user_name 的測試方法,它接受一個名為 mock_get 的參數(shù)
    def test_get_user_name(self, mock_get):
        # 配置 mock_get 的返回值,使其在調(diào)用 json 方法時返回一個包含 "name": "Alice" 的字典
        mock_get.return_value.json.return_value = {"name": "Alice"}

        # 調(diào)用 get_user_name 函數(shù),并傳入 user_id 參數(shù)
        user_name = get_user_name(1)

        # 使用 unittest.TestCase 的 assertEqual 方法檢查 get_user_name 的返回值是否等于 "Alice"
        self.assertEqual(user_name, "Alice")

        # 使用 unittest.mock.Mock 的 assert_called_with 方法檢查 mock_get 是否被正確調(diào)用
        mock_get.assert_called_with("https://api.example.com/users/1")

 

總結(jié)

在開發(fā)測試樁時,我們需要根據(jù)實際需求和后端服務(wù)的特點來設(shè)計測試樁的行為,為的是使其更接近實際后端服務(wù)的行為,確保測試結(jié)果具有更高的可靠性和準確性。

可能還有其他的優(yōu)化方案,歡迎大家提出。希望本文能對大家的工作帶來幫助。

轉(zhuǎn)載自:https://www.cnblogs.com/Detector/p/17557317.html