Integracja mitmproxy w testy automatyczne UI: zarządzanie ruchem dla inżynierów QA
Proxyowanie ruchu sieciowego to krytycznie ważne narzędzie do zwiększania efektywności testów automatycznych UI. W przeciwieństwie do testów manualnych, gdzie używa się Charles Proxy lub Fiddler, automatyzacja wymaga programowalnych rozwiązań. Mitmproxy, dzięki Python API i trybowi headless, umożliwia wdrożenie kontrolowanej warstwy sieciowej bezpośrednio w infrastrukturę testową.
Dlaczego proxy jest potrzebne w testach automatycznych
Testy automatyczne UI często cierpią na "ślepotę sieciową" – niezdolność do wykrywania problemów związanych z API, WebSocket czy backendem. Prowadzi to do flaky testów, fałszywych błędów i trudności w diagnozie. Integracja proxy rozwiązuje te problemy, dodając testom widoczność sieciową. Kluczowe zastosowania:
- Przechwytywanie i analiza żądań HTTP oraz odpowiedzi do weryfikacji zachowania aplikacji.
- Modyfikacja kodów statusu (np. 200 → 404) do testowania obsługi błędów po stronie klienta.
- Podmiana danych w odpowiedziach JSON/XML do symulacji stanów testowych bez użycia środowisk DEV.
- Logowanie ruchu i śledzenie sekwencji żądań.
- Praca z wiadomościami WebSocket, w tym wstrzykiwanie i modyfikacja w locie.
Wybór mitmproxy jako optymalnego rozwiązania
Charles, Proxyman i Fiddler nie nadają się do automatyzacji z powodu braku API do zewnętrznego zarządzania. Mitmproxy, jako narzędzie open-source, oferuje:
- Python API do pisania skryptów przechwytujących i modyfikujących ruch.
- Tryb headless do uruchamiania w środowiskach CI bez interfejsu graficznego.
- Obsługę WebSocket, co jest kluczowe dla nowoczesnych aplikacji mobilnych i webowych.
- Wieloplatformowość (Windows, Linux, macOS).
Mitmproxy działa w trzech trybach: mitmproxy (interaktywne CLI), mitmweb (interfejs webowy) i mitmdump (wyjście nieinteraktywne). Do testów automatycznych używa się mitmdump lub mitmweb z niestandardowymi skryptami.
Architektura integracji z frameworkiem testowym
Uruchomienie mitmproxy odbywa się jako osobny proces, niezależny od frameworka testowego. Interakcja między nimi organizowana jest przez plik konfiguracyjny (np. config.json) i moduł Python (proxy_handler.py). Podstawowy przepływ:
- Instalacja mitmproxy (np. przez
brew install mitmproxy). - Konfiguracja certyfikatów na urządzeniach testowych do przechwytywania ruchu HTTPS.
- Uruchomienie proxy komendą
mitmweb -s proxy_handler.py. - Dynamiczne zarządzanie zachowaniem proxy przez aktualizację config.json z testów.
Implementacja podmiany kodów statusu: praktyczny przykład
Rozważmy przypadek modyfikacji statusów HTTP dla konkretnych API. Konfiguracja ustawiana jest w config.json:
{
"status": {"api/v1/user": 404, "api/v2/settings": 500}
}
Do jednorazowej podmiany używa się wartości-numer, do długotrwałej – listy (np. [404]). Moduł proxy_handler.py obsługuje te ustawienia:
import re
import mitmproxy.ctx as ctx
from mitmproxy import http
from file_worker import FileWorker
file_worker = FileWorker()
def response(flow: http.HTTPFlow) -> None:
url = flow.request.url
if not flow.response.content:
return
cfg = file_worker.get_proxy_params()
cfg_status = cfg.get("status")
for api, sc in list(cfg_status.items()):
if bool(re.compile(api).search(url)):
flow.response.status_code = int(sc[0] if isinstance(sc, list) else sc)
ctx.log.info(f"Status code was mocked '{api}' -> {sc}")
if not isinstance(sc, list):
del cfg_status[api]
file_worker.set_proxy_param("status", cfg_status)
cfg["status"] = cfg_status
break
Kluczowe punkty implementacji:
- Użycie wyrażeń regularnych (moduł
re) do elastycznego dopasowania URI. - Obsługa obu formatów konfiguracji (jednorazowa i długotrwała podmiana).
- Logowanie operacji do debugowania.
- Wydzielenie pracy z plikami do osobnej klasy (FileWorker) dla czystości kodu.
Rozszerzenie funkcjonalności: mockowanie danych i logowanie
Analogicznie do podmiany statusów, można zaimplementować modyfikację treści odpowiedzi. W config.json dodaje się sekcję "mock":
{
"mock": {"api/v1/data": {"field": "new_value"}}
}
W proxy_handler.py obsługa wygląda tak:
cfg_mock: dict = cfg.get("mock")
if cfg_mock:
for mock_api, params in list(cfg_mock.items()):
if bool(re.compile(mock_api).search(url)):
data = flow.response.content.decode()
modified = file_worker.mock(params[0] if isinstance(params, list) else params, data)
if modified:
flow.response.content = modified.encode()
ctx.log.info(f"Param {params} were mocked for '{mock_api}'")
if not isinstance(params, list):
del cfg_mock[mock_api]
file_worker.set_proxy_param("mock", cfg_mock)
cfg["mock"] = cfg_mock
break
Do zapisywania odpowiedzi serwera do plików (np. do późniejszej analizy) używa się sekcji "get_response" w config.json. To jest przydatne dla:
- Weryfikacji struktury danych w automatycznych sprawdzeniach.
- Tworzenia wzorcowych odpowiedzi do testów regresyjnych.
- Debugowania skomplikowanych scenariuszy interakcji z backendem.
Co jest ważne
- Network visibility: Integracja proxy eliminuje "ślepotę" testów automatycznych do problemów sieciowych, poprawiając diagnozę i stabilność.
- Elastyczność mitmproxy: Python API i obsługa WebSocket umożliwiają tworzenie złożonych scenariuszy modyfikacji ruchu bez zależności od narzędzi GUI.
- Zarządzanie konfiguracyjne: Użycie config.json do dynamicznej zmiany zachowania proxy upraszcza integrację z frameworkami testowymi.
- Wydajność: Kod handlerów musi być zoptymalizowany, aby nie spowalniać działania proxy i testów.
- Rozszerzalność: Architektura oparta na modułach (proxy_handler.py, FileWorker) pozwala łatwo dodawać nową funkcjonalność, np. throttling czy analizę WebSocket.
Podsumowanie
Wdrożenie mitmproxy w testy automatyczne UI przekształca proxy z narzędzia manualnej analizy w kontrolowany komponent zautomatyzowanej weryfikacji. Znacząco rozszerza to możliwości testowania aplikacji klienckich, zwłaszcza w scenariuszach wymagających symulacji specyficznych stanów backendu lub szczegółowego monitorowania interakcji sieciowych. Dla udanej integracji kluczowe jest zachowanie równowagi między elastycznością konfiguracji a wydajnością handlerów ruchu.
— Editorial Team
Brak komentarzy.