Présentation des tests en Python. 2e partie

Bonjour Ă  tous!

Nous continuons l' article sur la connaissance des tests en Python, que nous avons préparé pour vous dans le cadre de notre cours de développeur Python .

Test pour Django et Flask Web Framework

Si vous écrivez des tests pour des applications Web à l'aide de l'un des frameworks populaires, par exemple, Django ou Flask, il convient de se rappeler les différences importantes dans l'écriture et l'exécution de ces tests.

Comment ils diffĂšrent des autres applications

Pensez au code que vous souhaitez tester dans une application Web. Tous les itinéraires, vues et modÚles nécessitent beaucoup d'importations et de connaissances sur le cadre utilisé.
Cela est similaire au test d'une voiture, qui a été discuté dans la premiÚre partie du didacticiel: avant d'effectuer des tests simples, tels que la vérification des phares, vous devez allumer l'ordinateur dans la voiture.

Django et Flask simplifient cette tùche et fournissent un cadre de test basé sur unittittest. Vous pouvez continuer à écrire des tests de la maniÚre habituelle, mais exécutez-les un peu différemment.



Comment utiliser le Django Test Executor

Le modÚle Django startapp crée le fichier tests.py dans votre répertoire d'application. S'il n'est pas déjà là, créez-le avec le contenu suivant:

from django.test import TestCase class MyTestCase(TestCase): # Your test methods 

La principale diffĂ©rence avec les exemples prĂ©cĂ©dents est que vous devez hĂ©riter de django.test.TestCase , et non unittest.TestCase . L'API de ces classes est la mĂȘme, mais la classe Django TestCase configure tout pour les tests.

Pour exécuter une suite de tests, utilisez le test manage.py au lieu de unittest sur la ligne de commande:

 $ python manage.py test 

Si vous avez besoin de plusieurs fichiers de test, remplacez tests.py par le dossier tests, placez-y un fichier vide appelé __init__.py et créez test_*.py fichiers test_*.py . Django les détectera et les exécutera.

Plus d'informations sont disponibles sur le site de documentation de Django .

Comment utiliser Unittest et Flask

Pour fonctionner avec Flask, une application doit ĂȘtre importĂ©e et mise en mode test. Vous pouvez crĂ©er un client de test et l'utiliser pour envoyer des demandes Ă  toutes les routes de votre application.

Le client de test est instancié dans la méthode setUp de votre scénario de test. Dans l'exemple suivant, my_app est le nom de l'application. Ne vous inquiétez pas si vous ne savez pas ce que fait la configuration. Nous examinerons de plus prÚs la section "Scripts de test plus avancés".
Le code du fichier de test ressemblera Ă  ceci:

 import my_app import unittest class MyTestCase(unittest.TestCase): def setUp(self): my_app.app.testing = True self.app = my_app.app.test_client() def test_home(self): result = self.app.get('/') # Make your assertions 

Vous pouvez ensuite exécuter des cas de test à l'aide de la commande python -m unittest discover.

Plus d'informations sont disponibles sur le site de documentation de Flask.

Scripts de test plus avancés

Avant de commencer à créer des tests pour votre application, n'oubliez pas les trois étapes principales de tout test:

  1. Création de paramÚtres d'entrée;
  2. Exécution de code, réception de données de sortie;
  3. Comparaison des données de sortie avec le résultat attendu;

Cela peut ĂȘtre plus compliquĂ© que de crĂ©er une valeur statique pour les donnĂ©es source comme une chaĂźne ou un nombre. Parfois, votre application nĂ©cessite une instance d'une classe ou d'un contexte. Que faire dans ce cas?

Les données que vous créez en tant que source sont appelées fixture. La création et la réutilisation de luminaires est une pratique courante.

L'exĂ©cution du mĂȘme test plusieurs fois avec des valeurs diffĂ©rentes en prĂ©vision du mĂȘme rĂ©sultat est appelĂ©e paramĂ©trage.

Gestion des Ă©checs attendus

Plus tÎt, lorsque nous avons compilé une liste de scripts pour tester sum() , la question s'est posée: que se passe-t-il lorsque nous fournissons une mauvaise valeur, par exemple, un seul entier ou une chaßne?

Dans ce cas, il est prévu que sum() génÚre une erreur. Si une erreur se produit, le test échouera.

Il existe un moyen spécifique de gérer les erreurs attendues. Vous pouvez utiliser .assertRaises() comme gestionnaire de contexte, puis effectuer des étapes de test à l'intérieur du bloc with :

 import unittest from my_sum import sum class TestSum(unittest.TestCase): def test_list_int(self): """ ,       """ data = [1, 2, 3] result = sum(data) self.assertEqual(result, 6) def test_list_fraction(self): """ ,       """ data = [Fraction(1, 4), Fraction(1, 4), Fraction(2, 5)] result = sum(data) self.assertEqual(result, 1) def test_bad_type(self): data = "banana" with self.assertRaises(TypeError): result = sum(data) if __name__ == '__main__': unittest.main() 

Ce cas de test ne sera réussi que si sum(data) lÚve une TypeError. Vous pouvez remplacer TypeError par tout autre type d'exception.

Isolement du comportement des applications

Dans la derniÚre partie du tutoriel, nous avons parlé des effets secondaires. Ils compliquent les tests unitaires, car chaque test peut produire un résultat différent ou pire - un test peut affecter l'état de l'application entiÚre et entraßner l'échec d'un autre test!

Il existe quelques techniques simples pour tester des parties d'une application avec beaucoup d'effets secondaires:

  • Refactorisation du code conformĂ©ment au principe de responsabilitĂ© unique;
  • Se moquer de toutes les mĂ©thodes et appels de fonction pour Ă©liminer les effets secondaires;
  • Utilisation de tests d'intĂ©gration au lieu de tests unitaires pour ce fragment de l'application.
  • Si vous n'ĂȘtes pas familier avec le moking, consultez quelques excellents exemples de test CLI Python .

Écriture de tests d'intĂ©gration

Jusqu'à présent, nous avons accordé plus d'attention aux tests unitaires. Les tests unitaires sont un excellent moyen de créer du code prévisible et stable. Mais au final, votre application devrait fonctionner au démarrage!

Des tests d'intégration sont nécessaires pour vérifier la collaboration de plusieurs composants d'application. Ces tests peuvent nécessiter de jouer le rÎle de l'acheteur ou de l'utilisateur:

  • Appelez l'API HTTP REST;
  • Appel API Python;
  • Appel de service Web;
  • ExĂ©cutez la ligne de commande.

Tous ces types de tests d'intĂ©gration peuvent ĂȘtre Ă©crits de la mĂȘme maniĂšre que les tests unitaires, en suivant le modĂšle ParamĂštres d'entrĂ©e, exĂ©cution, approbation. La diffĂ©rence la plus significative est que les tests d'intĂ©gration testent simultanĂ©ment plus de composants, ce qui signifie qu'ils entraĂźneront plus d'effets secondaires que les tests unitaires. De plus, les tests d'intĂ©gration nĂ©cessitent plus d'appareils, tels qu'une base de donnĂ©es, une prise rĂ©seau ou un fichier de configuration.

Par consĂ©quent, il est recommandĂ© de sĂ©parer les tests unitaires et les tests d'intĂ©gration. La crĂ©ation d'appareils pour l'intĂ©gration, par exemple, une base de donnĂ©es de test ou des cas de test eux-mĂȘmes, prend beaucoup plus de temps que d'effectuer des tests unitaires, vous devez donc effectuer des tests d'intĂ©gration avant de passer en production au lieu de les exĂ©cuter chaque fois que vous vous engagez.

Le moyen le plus simple de séparer les tests unitaires et d'intégration est de les placer dans des dossiers différents.

project/
│
├── my_app/
│ └── __init__.py
│
└── tests/
|
├── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
├── __init__.py
└── test_integration.py


Vous pouvez exĂ©cuter un groupe spĂ©cifique de tests de diffĂ©rentes maniĂšres. Un indicateur pour spĂ©cifier le rĂ©pertoire source, -s, peut ĂȘtre ajoutĂ© Ă  la dĂ©couverte la plus simple avec un chemin contenant des tests:

 $ python -m unittest discover -s tests/integration 

unittest affichera tous les résultats dans le répertoire tests / intégration.

Test des applications orientées données

De nombreux tests d'intĂ©gration nĂ©cessitent des donnĂ©es dorsales, telles qu'une base de donnĂ©es avec des valeurs spĂ©cifiques. Supposons que vous ayez besoin d'un test pour vĂ©rifier le bon fonctionnement de l'application avec plus de 100 clients dans la base de donnĂ©es, ou pour vĂ©rifier l'exactitude de l'affichage de la page de commande, mĂȘme si tous les noms des marchandises sont en japonais.

Ces types de tests d'intégration dépendront de divers montages de test pour assurer leur répétabilité et leur prévisibilité.

Les donnĂ©es de test doivent ĂȘtre stockĂ©es dans le dossier fixtures Ă  l'intĂ©rieur du rĂ©pertoire des tests d'intĂ©gration pour souligner leur «testabilité». Ensuite, dans les tests, vous pouvez charger les donnĂ©es et exĂ©cuter le test.

Voici un exemple de structure de données composée de fichiers JSON:

project/
│
├── my_app/
│ └── __init__.py
│
└── tests/
|
└── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
|
├── fixtures/
| ├── test_basic.json
| └── test_complex.json
|
├── __init__.py
└── test_integration.py


Dans le cas de test, vous pouvez utiliser la méthode .setUp () pour charger les données de test à partir du fichier fixture d'une maniÚre connue et exécuter plusieurs tests avec ces données. N'oubliez pas que vous pouvez stocker plusieurs cas de test dans un fichier Python, unittest les trouvera et les exécutera. Vous pouvez avoir un cas de test pour chaque ensemble de données de test:

 import unittest class TestBasic(unittest.TestCase): def setUp(self): # Load test data self.app = App(database='fixtures/test_basic.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 100) def test_existence_of_customer(self): customer = self.app.get_customer(id=10) self.assertEqual(customer.name, "Org XYZ") self.assertEqual(customer.address, "10 Red Road, Reading") class TestComplexData(unittest.TestCase): def setUp(self): # load test data self.app = App(database='fixtures/test_complex.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 10000) def test_existence_of_customer(self): customer = self.app.get_customer(id=9999) self.assertEqual(customer.name, u"バナナ") self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo") if __name__ == '__main__': unittest.main() 

Si votre application dĂ©pend des donnĂ©es d'un emplacement distant, comme une API distante, assurez-vous que les tests sont rĂ©pĂ©tables. Le dĂ©veloppement peut ĂȘtre retardĂ© en raison d'Ă©checs de tests lors de la dĂ©sactivation de l'API et de problĂšmes de communication. Dans de tels cas, il est prĂ©fĂ©rable de stocker localement les appareils distants pour ĂȘtre rappelĂ© et envoyĂ© Ă  l'application.

La bibliothÚque de requests dispose d'un package de réponses gratuit qui vous permet de créer des fixtures de réponse et de les enregistrer dans des dossiers de test. En savoir plus sur leur page GitHub .

La prochaine partie portera sur les tests dans plusieurs environnements et les tests d'automatisation.

LA FIN

Les commentaires / questions sont toujours les bienvenus. Ici ou allez à Stas lors d'une journée portes ouvertes .

Source: https://habr.com/ru/post/fr433572/


All Articles