paint-brush
Tests de performances basés sur Python pour les testeurs d'assurance qualité : guide du débutant sur les tests de charge des API Cloudpar@shad0wpuppet
27,071 lectures
27,071 lectures

Tests de performances basés sur Python pour les testeurs d'assurance qualité : guide du débutant sur les tests de charge des API Cloud

par Konstantin Sakhchinskiy8m2024/01/19
Read on Terminal Reader
Read this story w/o Javascript

Trop long; Pour lire

Explorez les scripts Python pour les testeurs d'assurance qualité effectuant des tests de charge sur les API d'applications cloud. L'article couvre les approches asynchrones et multitraitements, offrant des informations sur la personnalisation, la sélection de méthodologie et des conseils pratiques de surveillance. Tirez parti des capacités techniques de Python pour une analyse complète des performances.
featured image - Tests de performances basés sur Python pour les testeurs d'assurance qualité : guide du débutant sur les tests de charge des API Cloud
Konstantin Sakhchinskiy HackerNoon profile picture
0-item
1-item


Êtes-vous un testeur QA désireux de vous lancer dans les tests de performances sans avoir besoin d’une expertise approfondie en programmation ? Dans cet article, nous explorerons un moyen accessible aux non-programmeurs d'effectuer des tests de charge sur les API des applications cloud à l'aide de Python . Tests de charge sans avoir besoin d'un codage complexe : découvrez comment même les testeurs d'assurance qualité réguliers peuvent utiliser Python pour détecter des bogues graves et découvrir d'éventuels goulots d'étranglement en matière de performances.


Les tests de performances sont un aspect essentiel pour garantir que vos applications peuvent répondre aux demandes du monde réel. Je vais essayer d'expliquer mon approche et les scripts Python conçus pour tester en charge un service cloud gérant les navigateurs.


Scénario de test de charge Imaginez un service cloud chargé de gérer les profils de navigateur (navigateurs pour la mise au rebut Web). Les utilisateurs interagissent via l'API avec le service pour créer, démarrer, arrêter, supprimer, etc. des profils. Mon script Python simule ce scénario, appliquant une charge au service cloud en effectuant ces actions à plusieurs reprises.


 # Dependencies import asyncio import httpx # Configuration API_HOST = 'https://cloud.io' API_KEY = 'qatest' API_HEADERS = { "x-cloud-api-token": API_KEY, "Content-Type": "application/json" } CYCLES_COUNT = 3 # Browser profile configuration data_start = { "proxy": "http://127.0.0.1:8080", "browser_settings": {"inactive_kill_timeout": 120} }

Fonctions asynchrones : Le cœur de mes tests de charge

  • Récupération des profils de navigateur : la fonction get_profiles récupère les profils de navigateur existants du service, simulant un scénario dans lequel les utilisateurs demandent des informations.
 async def get_profiles(cl: httpx.AsyncClient): resp = await cl.get(f'{API_HOST}/profiles', params={'page_len': 10, 'page': 0}, headers=API_HEADERS) return resp.json()


  • Démarrage des navigateurs : le script lance des cycles, chacun impliquant le démarrage de profils de navigateur de manière asynchrone. Cela émule un scénario dans lequel un utilisateur crée plusieurs navigateurs simultanément et utilise des profils de navigateur.
 async def start_profile(cl: httpx.AsyncClient, uuid): resp = await cl.post(f'{API_HOST}/profiles/{id}/start', json=data_start, headers=API_HEADERS) if error := resp.json().get('error'): print(f'Profile {id} not started with error {error}')


  • Arrêt des navigateurs et suppression de profils : après le démarrage des profils, le script récupère les profils actifs et les arrête, puis supprime tous les profils. Ce scénario de charge évalue la réactivité du service cloud aux changements dynamiques et son efficacité en matière de nettoyage des ressources.
 async def stop_profile(cl: httpx.AsyncClient, uuid): resp = await cl.post(f'{API_HOST}/profiles/{id}/stop', headers=API_HEADERS) if error := resp.json().get('error'): print(f'Profile {id} not stopped with error {error}') async def delete_profile(cl: httpx.AsyncClient, uuid): resp = await cl.delete(f'{API_HOST}/profiles/{id}', headers=API_HEADERS) if error := resp.json().get('error'): print(f'Profile {id} not stopped with error {error}')


  • Surveillance des connexions : comprendre l'impact de la charge Le script se termine par la vérification et la création de rapports sur les connexions actives. Cette étape est cruciale pour comprendre l’impact de la charge et identifier les problèmes potentiels liés à la surveillance.
 for conn in cl._transport._pool.connections: if conn._connection._state.value != 1: continue print(f'Connection in progress: {conn}')

Tests de charge en action

La fonction principale orchestre les cycles de tests de charge, en parcourant les profils et en exécutant des tâches asynchrones. Chaque cycle représente une interaction utilisateur simulée, créant, utilisant et supprimant des profils de navigateur.


 async def main(): async with httpx.AsyncClient(timeout=httpx.Timeout(timeout=300)) as cl: for _ in range(CYCLES_COUNT): profiles = await get_profiles(cl) start_tasks = [asyncio.create_task(start_profile(cl, profile['id'])) for profile in profiles] await asyncio.gather(*start_tasks) active_browsers = await get_active_profiles(cl) stop_tasks = [asyncio.create_task(stop_profile(cl, active_browser['id'])) for active_browser in active_browsers['data']] await asyncio.gather(*stop_tasks) profiles = await get_profiles(cl) del_tasks = [asyncio.create_task(delete_profile(cl, profile['id'])) for profile in profiles] await asyncio.gather(*del_tasks) # Monitor active connections for insights into load impact

Adaptation des tests de charge à vos besoins

Ce script montre une base permettant aux responsables de l'assurance qualité d'adapter les scénarios de tests de charge à leurs applications. En personnalisant le nombre de cycles, en ajustant les interactions des utilisateurs et en modifiant le script pour l'adapter à des points de terminaison d'API spécifiques, les testeurs peuvent obtenir des informations précieuses sur les performances de leur application sous différentes charges. Ici, vous aurez besoin d'outils de surveillance essentiels pour obtenir des informations sur l'état du serveur, évaluer la charge du serveur et suivre l'utilisation des ressources et les journaux. Utilisez des outils tels que Grafana, Kibana, Prometheus, etc. pour une surveillance complète. De plus, surveillez de près les réponses reçues par votre script, garantissant ainsi une évaluation approfondie des performances de votre application. Cette approche est inestimable pour vos tests de charge efficaces et votre analyse des performances.


De plus, pour une simulation de charge plus réaliste, pensez à ouvrir des pages spécifiques dans votre navigateur. Bien que j'aie personnellement utilisé une page de démarrage dans mes navigateurs, vous pouvez également explorer des options telles que Pyppeteer ou Playwright pour ouvrir plusieurs onglets et naviguer dans différentes pages. Cette approche améliore l'authenticité de votre scénario de test de charge, ressemblant étroitement aux interactions des utilisateurs avec votre application.


 # Attempt to connect to the browser using the provided profile URL try: browser = await connect({'browserWSEndpoint': browser_url, 'defaultViewport': None}) except Exception as e: # Handle connection errors and print a message print(f'Error occurred when connecting to the browser: {str(e)}') return # Create a new page in the connected browser page = await browser.newPage() # Introduce a brief delay to ensure the page is ready await asyncio.sleep(2) # Set the viewport dimensions for the page width, height = 1920, 1080 await page.setViewport({'width': width, 'height': height}) # Try to navigate to a specific URL try: await page.goto('https://{your_website}') # Wait for 10 seconds to simulate user interaction await page.waitFor(10000) # Introduce another delay for additional stability await asyncio.sleep(5) except pyppeteer.errors.PageError as e: # Handle page navigation errors and print a message print(f'Error occurred during page navigation: {str(e)}') # Attempt to take a screenshot of the page try: await page.screenshot(path='screen.png', fullPage=True) # Print a success message if the screenshot is captured successfully print('Screenshot taken successfully.') except Exception as e: # Handle screenshot capture errors and print a message print(f'Error occurred during taking a screenshot: {str(e)}')

Les capacités asynchrones de Python, associées aux bibliothèques HTTP, en font un outil polyvalent pour tester la charge des systèmes basés sur le cloud. Cet exemple sert de point de départ aux ingénieurs QA cherchant à découvrir la puissance de Python dans leurs tentatives de tests de charge.


NOTE

Dans mon scénario, le script décrit s'est avéré robuste et percutant. Il s’est avéré un outil utile pour identifier et résoudre de nombreux problèmes. La nature agressive du script a permis d'identifier les problèmes critiques, de faciliter un débogage efficace et d'ouvrir la voie à une expérience utilisateur transparente et améliorée, ce qui est plutôt bon pour un contrôle qualité.



Dans la suite, je discuterai brièvement d'un autre script utilisant le module multitraitement de Python. Cette approche vise à améliorer la génération de charge en exécutant simultanément plusieurs instances du script de test. L'objectif principal du multitraitement est de paralléliser l'exécution d'un script, permettant des interactions simultanées avec le service. Cette approche contraste avec l'approche asynchrone évoquée précédemment, dans laquelle les tâches sont exécutées séquentiellement mais gérées simultanément. Cela ressemble plus à du spam/ddos avec les mêmes requêtes, mais cela peut aussi être très utile.

Éléments essentiels

  • Fonction pour récupérer les profils : Semblable au script asynchrone, nous devons toujours récupérer les profils de navigateur existants à partir du service cloud.
 def get_profiles(): response = requests.get(url=f"{api}", params=PARAMS, headers=headers) return response


  • Fonctions : Le cœur du script s'articule autour de deux fonctions : start_profiles et stop_profiles. Ces fonctions lancent et terminent respectivement les profils de navigateur.
 def start_profiles(list_of_profiles_uuids): for uuid in list_of_profiles_uuids: # ... (API request to start profile) def stop_profiles(internal_uuids): for uuid in internal_uuids: # ... (API request to stop profile) def run_script(): start_profiles(get_profile_ids()) stop_profiles(list_of_ids)


  • Exécution multitraitement : le script utilise le module multitraitement pour exécuter le cycle de test de charge plusieurs fois en parallèle. Pour chaque cycle, un nouveau processus est généré et la fonction run_script est exécutée simultanément.
 if __name__ == "__main__": for runs in range(0, 5): processes = [] for i in range(20): p = multiprocessing.Process(target=run_script) processes.append(p) p.start() for p in processes: p.join()


Comment générer une charge avec le multitraitement

  1. Exécution parallèle : chaque processus généré par le module multitraitement fonctionne indépendamment, exécutant simultanément la fonction run_script. Cette parallélisation augmente considérablement le nombre de requêtes envoyées simultanément au service.
  2. Simulation des interactions utilisateur : en exécutant plusieurs instances du script en parallèle, nous simulons un volume plus élevé d'utilisateurs interagissant simultanément avec le service cloud. Cette approche est particulièrement utile pour les scénarios dans lesquels l'utilisation réelle implique un grand nombre d'utilisateurs simultanés.

Choisir la bonne approche

Le multitraitement fournit une stratégie pour les applications de test de charge. Il permet aux ingénieurs QA d’expérimenter différentes méthodologies en fonction des caractéristiques uniques de leurs applications. Alors que les tests asynchrones offrent une gestion efficace des tâches simultanées, le multitraitement excelle dans la parallélisation de l'ensemble du processus de test. Vous pouvez choisir l’approche qui correspond le mieux à leurs objectifs spécifiques en matière de tests de charge et aux exigences de leurs applications.


Un petit rappel :

Cette démo vise à présenter les concepts de base dans un format convivial pour les débutants, en soulignant la simplicité de Python pour les testeurs QA qui s'aventurent dans les tests de performances.

Si vous rencontrez des problèmes de programmation, n'hésitez pas à rechercher des informations sur Google et à demander à vos collègues, à utiliser ChatGPT ou des outils similaires, et à utiliser GitHub Copilot pour une assistance supplémentaire dans l'écriture de vos scripts de test de charge.


Également publié ici .