Question Exécution d'unittest avec une structure de répertoire de test typique


La structure de répertoires très courante, même pour un simple module Python, semble être de séparer les tests unitaires en leurs propres test annuaire:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

par exemple voir ceci Howto du projet Python.

Ma question est simplement Quelle est la manière habituelle d'exécuter réellement les tests? Je pense que c'est évident pour tout le monde sauf moi, mais vous ne pouvez pas courir python test_antigravity.py du répertoire de test comme son import antigravity échouera car le module n'est pas sur le chemin.

Je sais que je pourrais modifier PYTHONPATH et d'autres astuces liées aux chemins de recherche, mais je ne peux pas croire que ce soit le plus simple - c'est bien si vous êtes développeur mais pas réaliste à attendre de vos utilisateurs s'ils veulent vérifier qui passe.

L'autre alternative est juste de copier le fichier de test dans l'autre répertoire, mais il semble un peu bête et manque le point de les avoir dans un répertoire séparé pour commencer.

Donc, si vous veniez de télécharger la source sur mon nouveau projet, comment exécuteriez-vous les tests unitaires? Je préférerais une réponse qui me permettrait de dire à mes utilisateurs: "Pour exécuter les tests unitaires, faites X."


486
2017-12-13 16:10


origine


Réponses:


La meilleure solution à mon avis est d'utiliser le unittest  interface de ligne de commande qui va ajouter le répertoire à la sys.path de sorte que vous n'avez pas à (fait dans le TestLoader classe).

Par exemple pour une structure de répertoire comme celle-ci:

new_project
├── antigravity.py
└── test_antigravity.py

Vous pouvez simplement exécuter:

$ cd new_project
$ python -m unittest test_antigravity

Pour une structure de répertoire comme la vôtre:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

Et dans les modules de test à l'intérieur du test paquet, vous pouvez importer le antigravity package et ses modules comme d'habitude:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

Exécuter un seul module de test:

Pour exécuter un seul module de test, dans ce cas test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Référencez simplement le module de test de la même manière que vous l'importez.

Exécuter un seul test ou méthode de test:

Aussi, vous pouvez exécuter un seul TestCase ou une seule méthode de test:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

Exécuter tous les tests:

Vous pouvez aussi utiliser découverte de test qui va découvrir et exécuter tous les tests pour vous, ils doivent être des modules ou des packages nommés test*.py (peut être changé avec le -p, --pattern drapeau):

$ cd new_project
$ python -m unittest discover

Cela courra tous les test*.py modules à l'intérieur du test paquet.


452
2018-06-17 14:49



La solution la plus simple pour vos utilisateurs est de fournir un script exécutable (runtests.py ou un tel) qui amorce l'environnement de test nécessaire, y compris, si nécessaire, ajouter temporairement votre répertoire de projet racine à sys.path. Cela ne nécessite pas que les utilisateurs définissent des variables d'environnement, quelque chose comme cela fonctionne très bien dans un script bootstrap:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

Ensuite, vos instructions à vos utilisateurs peuvent être aussi simples que "python runtests.py".

Bien sûr, si le chemin que vous avez besoin est vraiment os.path.dirname(__file__), alors vous n'avez pas besoin de l'ajouter à sys.path du tout; Python place toujours le répertoire du script en cours au début de sys.path, donc en fonction de la structure de votre répertoire, juste localiser votre runtests.py au bon endroit pourrait être tout ce qui est nécessaire.

Également module unittest en Python 2.7+ (qui est rétroportée comme unittest2 pour Python 2.6 et versions antérieures) a maintenant découverte de test intégré, donc nez n'est plus nécessaire si vous voulez une découverte de test automatisée: vos instructions utilisateur peuvent être aussi simples que "python -m unittest discover".


43
2017-12-13 20:40



Je crée généralement un script "exécuter des tests" dans le répertoire du projet (celui qui est commun à la fois le répertoire source et test) qui charge ma suite "Tous les tests". Ceci est généralement un code passe-partout, donc je peux le réutiliser de projet en projet.

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test / all_tests.py (de Comment exécuter tous les tests unitaires Python dans un répertoire?)

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

Avec cette configuration, vous pouvez en effet simplement include antigravity dans vos modules de test. L'inconvénient est que vous auriez besoin de plus de code de support pour exécuter un test particulier ... Je les exécute tous à chaque fois.


18
2018-06-07 19:30



De l'article que vous avez lié à:

Créez un fichier test_modulename.py et   mettez vos tests unittest dedans. Depuis   les modules de test sont dans une   répertoire de votre code, vous pouvez avoir besoin   ajouter le répertoire parent de votre module   à votre PYTHONPATH pour courir   leur:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

Enfin, il y en a un de plus populaire   framework de test unitaire pour Python   (c'est si important!), nez. nez   permet de simplifier et d'étendre le builtin   cadre d'unittest (il peut, pour   Par exemple, trouvez automatiquement votre test   codez et configurez votre PYTHONPATH pour   vous), mais il n'est pas inclus avec le   distribution Python standard.

Peut-être devriez-vous regarder nez comme il suggère?


17
2017-12-13 16:25



Si vous lancez "python setup.py develop", le paquet sera dans le chemin. Mais vous ne voulez peut-être pas faire cela parce que vous pourriez infecter l'installation de votre système python, ce qui explique pourquoi des outils comme virtualenv et construire sur exister.


6
2017-12-13 16:27



J'ai eu le même problème, avec un dossier de tests unitaires séparé. Parmi les suggestions mentionnées, j'ajoute le chemin source absolu à sys.path.

L'avantage de la solution suivante est, que l'on peut exécuter le fichier test/test_yourmodule.py sans changer d'abord dans le répertoire de test:

import sys, os
testdir = os.path.dirname(__file__)
srcdir = '../src/projekt/dir'
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))

import yourmodule
import unittest

5
2017-12-04 09:45



Utilisation setup.py develop Pour que votre répertoire de travail fasse partie de l'environnement Python installé, lancez les tests.


4
2017-12-13 16:24



Si vous utilisez le code VS et que vos tests se situent au même niveau que votre projet, alors l'exécution et le débogage de votre code ne fonctionnent pas immédiatement. Ce que vous pouvez faire est de changer votre fichier launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "program": "${file}",
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.env",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }    
    ]
}

La ligne clé ici est envFile

"envFile": "${workspaceRoot}/.env",

A la racine de votre projet, ajoutez le fichier .env

À l'intérieur de votre fichier .env, ajoutez le chemin à la racine de votre projet. Cela va ajouter temporairement

PYTHONPATH = C: \ VOTRE \ PYTHON \ PROJECT \ ROOT_DIRECTORY

chemin d'accès à votre projet et vous serez en mesure d'utiliser les tests d'unité de débogage de VS Code


4
2018-05-27 23:45



Solution / Exemple pour Python unittest module

Compte tenu de la structure de projet suivante:

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

Vous pouvez exécuter votre projet à partir du répertoire racine avec python project_name, qui appelle ProjectName/project_name/__main__.py.


Pour exécuter vos tests avec python test, courir efficacement ProjectName/test/__main__.py, vous devez faire ce qui suit:

1) Tourne ton test/models répertoire dans un paquet en ajoutant un __init__.py fichier. Cela rend les cas de test dans le sous-répertoire accessible à partir du parent test annuaire.

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2) Modifiez le chemin de votre système dans test/__main__.py pour inclure le project_name annuaire.

# ProjectName/test/__main__.py

import sys
import unittest

sys.path.append('../project_name')

loader = unittest.TestLoader()
testSuite = loader.discover('test')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)

Vous pouvez maintenant importer des objets depuis project_name dans tes tests.

# ProjectName/test/models/test_thing_1.py    

import unittest
from project_name.models import Thing1  # this doesn't work without 'sys.path.append' per step 2 above

class Thing1TestCase(unittest.TestCase):

    def test_thing_1_init(self):
        thing_id = 'ABC'
        thing1 = Thing1(thing_id)
        self.assertEqual(thing_id, thing.id)

3
2018-06-28 23:45



Voici la structure de mon projet:

ProjectFolder:
 - project:
     - __init__.py
     - item.py
 - tests:
     - test_item.py

J'ai trouvé mieux d'importer dans la méthode setUp ():

import unittest
import sys    

class ItemTest(unittest.TestCase):

    def setUp(self):
        sys.path.insert(0, "../project")
        from project import item
        # further setup using this import

    def test_item_props(self):
        # do my assertions

if __name__ == "__main__":
    unittest.main()

2
2017-07-25 14:01



Quelle est la manière habituelle d'exécuter les tests?

J'utilise Python 3.6.2

cd new_project

pytest test/test_antigravity.py

À installer pytest: sudo pip install pytest

Je n'ai défini aucune variable de chemin et mes importations n'échouent pas avec la même structure de projet "test".

J'ai commenté ce genre de choses: if __name__ == '__main__' comme ça:

test_antigravity.py

import antigravity

class TestAntigravity(unittest.TestCase):

    def test_something(self):

        # ... test stuff here


# if __name__ == '__main__':
# 
#     if __package__ is None:
# 
#         import something
#         sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
#         from .. import antigravity
# 
#     else:
# 
#         from .. import antigravity
# 
#     unittest.main()

2
2017-07-26 21:43