Question

J'ai un répertoire qui contient mes tests unitaires Python. Chaque module de test unitaire est de la forme Test _ *. Py . Je cherche à faire un fichier appelé all_test.py cette volonté, vous l'aurez deviné, exécutez tous les fichiers sous forme de test ci-dessus et retourner le résultat. J'ai essayé deux méthodes à ce jour; les deux ont échoué. Je vais montrer les deux méthodes, et j'espère que quelqu'un sait comment là réellement faire cela correctement.

Pour ma première tentative courageuse, je pensais: « Si j'importer simplement tous mes modules de test dans le fichier, puis appeler cette doodad de unittest.main(), il fonctionnera, non? » Eh bien, tourne que j'avais tort.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]

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

Cela ne fonctionne pas, le résultat que je suis arrivé était:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Pour mon deuxième essai, je pensais, ok, peut-être que je vais essayer de faire cette chose d'essai tout en plus la mode « manuel ». Donc, je tentais de le faire ci-dessous:

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite 

result = unittest.TestResult()
testSuite.run(result)
print result

#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __name__ == "__main__":
    unittest.main()

aussi ne fonctionnait pas, mais il semble si proche!

$ python all_test.py 
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Il me semble avoir une suite de quelque sorte, et je peux exécuter le résultat. Je suis un peu préoccupé par le fait qu'il dit que je n'ai que run=1, semble comme ça devrait être run=2, mais il est un progrès. Mais comment puis-je passer et afficher le résultat principal? Ou comment puis-je le faire fonctionner essentiellement je peux simplement exécuter ce fichier, et ce faisant, exécuter tous les tests unitaires dans ce répertoire?

Était-ce utile?

La solution

Vous pouvez utiliser un coureur de test qui ferait cela pour vous. est très bon par exemple. Lorsqu'il est exécuté, il trouvera des tests dans l'arborescence en cours et de les exécuter.

Mise à jour:

Voici un code de mes jours pré-nez. Vous ne voulez probablement pas la liste explicite des noms de modules, mais peut-être que le reste sera utile pour vous.

testmodules = [
    'cogapp.test_makefiles',
    'cogapp.test_whiteutils',
    'cogapp.test_cogapp',
    ]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        # If the module defines a suite() function, call it to get the suite.
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        # else, just load all the test cases from the module.
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

Autres conseils

Avec Python 2.7 et plus vous n'avez pas d'écrire un nouveau code ou utiliser des outils tiers pour le faire; exécution d'un test récursif via la ligne de commande est intégrée.

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'

Vous pouvez en lire plus dans le python 2.7 ou python 3.x unittest documentation.

Il est désormais possible directement à partir unittest: unittest.TestLoader. découvrir .

import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)

En python 3, si vous utilisez unittest.TestCase:

  • Vous devez avoir un vide (ou autre) fichier __init__.py dans votre répertoire test ( doit être nommé test/)
  • Vos fichiers de test à l'intérieur test/ correspondent au modèle test_*.py. Ils peuvent être à l'intérieur d'un sous-répertoire test/, et ces subdirs peut être nommé comme quoi que ce soit.

Ensuite, vous pouvez exécuter tous les tests avec:

python -m unittest

Fait! Une solution à moins de 100 lignes. Si tout va bien un autre débutant python fait gagner du temps en trouvant cela.

Et bien en étudiant le code ci-dessus un peu (en particulier en utilisant TextTestRunner et defaultTestLoader), je suis en mesure d'obtenir assez proche. Finalement, je fixe mon code par également de passage toutes les suites de test à un seul constructeur de suites, plutôt que de les ajouter « manuellement », qui fixe mes autres problèmes. Alors, voici ma solution.

import glob
import unittest

test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)

Oui, il est probablement plus facile à utiliser juste le nez que pour ce faire, mais qui est d'ailleurs le point.

Si vous voulez exécuter tous les tests de différentes classes de cas de test et vous êtes heureux de les spécifier explicitement alors vous pouvez le faire comme ceci:

from unittest import TestLoader, TextTestRunner, TestSuite
from uclid.test.test_symbols import TestSymbols
from uclid.test.test_patterns import TestPatterns

if __name__ == "__main__":

    loader = TestLoader()
    tests = [
        loader.loadTestsFromTestCase(test)
        for test in (TestSymbols, TestPatterns)
    ]
    suite = TestSuite(tests)

    runner = TextTestRunner(verbosity=2)
    runner.run(suite)

uclid est mon projet et TestSymbols et TestPatterns sont sous-classes de TestCase.

Je l'ai utilisé la méthode discover et une surcharge de load_tests pour atteindre ce résultat dans un (minimum, je pense) numéroter les lignes de code:

def load_tests(loader, tests, pattern):
''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/``
'''
    suite = TestSuite()
    for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'):
        for test_suite in all_test_suite:
            suite.addTests(test_suite)
    return suite

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

Exécution sur fives quelque chose comme

Ran 27 tests in 0.187s
OK

J'ai essayé différentes approches, mais tous semblent défectueux ou je dois maquillage un peu de code, qui est ennuyeux. Mais il y a un moyen convinient sous Linux, qui est tout simplement de trouver tous les tests par certain modèle, puis les appeler un par un.

find . -name 'Test*py' -exec python '{}' \;

et surtout, cela fonctionne certainement.

En cas de emballés bibliothèque ou une application, vous ne voulez pas le faire. setuptools va le faire pour vous .

  

Pour utiliser cette commande, votre tests de projet doivent être emballés dans une suite de test de unittest soit par une fonction, une classe de TestCase ou d'une méthode, ou un module ou package contenant des classes de TestCase. Si la suite du nom est un module, et le module a une fonction additional_tests(), elle est appelée et le résultat (qui doit être un unittest.TestSuite) est ajouté aux tests à exécuter. Si la suite du nom est un paquet, et les sous-modules sont récursive ajoutés sous-paquets à la suite de tests d'ensemble .

Il suffit de lui dire où votre package de test racine est, comme:

setup(
    # ...
    test_suite = 'somepkg.test'
)

run python setup.py test.

découverte basée sur les fichiers peut être problématique en Python 3, à moins que vous évitez les importations relatives dans votre suite de tests, parce que discover utilise l'importation de fichiers. Même si elle prend en charge top_level_dir en option, mais j'ai eu quelques erreurs de récursion infinie. Ainsi, une solution simple pour un code non emballé est de mettre ce qui suit dans __init__.py de votre package de test (voir Protocole load_tests Les).

import unittest

from . import foo, bar


def load_tests(loader, tests, pattern):
    suite = unittest.TestSuite()
    suite.addTests(loader.loadTestsFromModule(foo))
    suite.addTests(loader.loadTestsFromModule(bar))

    return suite

J'utilise PyDev / LiClipse et n'ont pas vraiment compris comment exécuter tous les tests à la fois de l'interface graphique. (Edit: vous cliquez droit sur le dossier de test de racine et choisissez Run as -> Python unit-test

Voici ma solution actuelle:

import unittest

def load_tests(loader, tests, pattern):
    return loader.discover('.')

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

Je mets ce code dans un module appelé all dans mon répertoire de test. Si je lance ce module comme unittest de LiClipse alors tous les tests sont exécutés. Si je demande seulement répéter des tests spécifiques ou échoué alors que les tests sont exécutés. Il n'a pas interférer avec mon coureur de test (soit commandline nosetests) -. Elle est ignorée

Vous devrez peut-être modifier les arguments pour discover en fonction de la configuration de votre projet.

Selon la réponse de Stephen Cagle J'ai ajouté le support des modules de test imbriqués.

import fnmatch
import os
import unittest

def all_test_modules(root_dir, pattern):
    test_file_names = all_files_in(root_dir, pattern)
    return [path_to_module(str) for str in test_file_names]

def all_files_in(root_dir, pattern):
    matches = []

    for root, dirnames, filenames in os.walk(root_dir):
        for filename in fnmatch.filter(filenames, pattern):
            matches.append(os.path.join(root, filename))

    return matches

def path_to_module(py_file):
    return strip_leading_dots( \
        replace_slash_by_dot(  \
            strip_extension(py_file)))

def strip_extension(py_file):
    return py_file[0:len(py_file) - len('.py')]

def replace_slash_by_dot(str):
    return str.replace('\\', '.').replace('/', '.')

def strip_leading_dots(str):
    while str.startswith('.'):
       str = str[1:len(str)]
    return str

module_names = all_test_modules('.', '*Tests.py')
suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname 
    in module_names]

testSuite = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=1)
runner.run(testSuite)

Le code recherche tous les sous-répertoires de . pour les fichiers *Tests.py qui sont ensuite chargés. Il attend chaque *Tests.py contienne une seule *Tests(unittest.TestCase) de classe qui est chargé à son tour et exécuté un après l'autre.

Cela fonctionne avec imbrications arbitraire des répertoires / modules, mais chaque répertoire doit contenir entre un fichier __init__.py vide au moins. Cela permet à l'essai pour charger les modules emboîtés par le remplacement des barres obliques (ou barres obliques inverses) par des points (voir replace_slash_by_dot).

Parce que la découverte de test semble être un sujet complet, il y a un cadre dédié à la découverte tester:

En savoir plus lire ici: https://wiki.python.org/moin/PythonTestingToolsTaxonomy

Ce script BASH exécutera le python unittest répertoire test partout dans le système de fichiers, peu importe quel répertoire de travail que vous êtes dans:. Son répertoire de travail toujours où ce répertoire test est situé

tous les tests, indépendants PWD $

unittest le module Python est sensible à votre répertoire en cours, à moins que vous dire où (en utilisant l'option discover -s).

Ceci est utile lors d'un séjour dans le ./src ou répertoire de travail ./example et vous avez besoin d'un test unitaire global rapide:

#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

Les tests sélectionnés, PWD indépendant $

Je baptise ce fichier utilitaire: runone.py et l'utiliser comme ceci:

runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

(cd "$dirname"/test; python -m unittest $1)

Pas besoin d'un fichier test/__init__.py à la charge de votre colis / mémoire en tête lors de la production.

Voici mon approche en créant un emballage pour exécuter les tests de la ligne de commande:

#!/usr/bin/env python3
import os, sys, unittest, argparse, inspect, logging

if __name__ == '__main__':
    # Parse arguments.
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument("-d", "--debug",    action="store_true", dest="debug",    help="show debug messages" )
    parser.add_argument("-h", "--host",     action="store",      dest="host",     help="Destination host" )
    parser.add_argument("-b", "--browser",  action="store",      dest="browser",  help="Browser driver.", choices=["Firefox", "Chrome", "IE", "Opera", "PhantomJS"] )
    parser.add_argument("-r", "--reports-dir", action="store",   dest="dir",      help="Directory to save screenshots.", default="reports")
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    # Load files from the arguments.
    for filename in args.files:
        exec(open(filename).read())

    # See: http://codereview.stackexchange.com/q/88655/15346
    def make_suite(tc_class):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(tc_class)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(tc_class(name, cargs=args))
        return suite

    # Add all tests.
    alltests = unittest.TestSuite()
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj) and name.startswith("FooTest"):
            alltests.addTest(make_suite(obj))

    # Set-up logger
    verbose = bool(os.environ.get('VERBOSE', args.verbose))
    debug   = bool(os.environ.get('DEBUG', args.debug))
    if verbose or debug:
        logging.basicConfig( stream=sys.stdout )
        root = logging.getLogger()
        root.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
        root.addHandler(ch)
    else:
        logging.basicConfig(stream=sys.stderr)

    # Run tests.
    result = unittest.TextTestRunner(verbosity=2).run(alltests)
    sys.exit(not result.wasSuccessful())

Par souci de simplicité, s'il vous plaît excuser mon pep8 de codage des normes .

Ensuite, vous pouvez créer la classe BaseTest pour les composants communs pour tous vos tests, donc chacun de votre test serait simplement ressembler à:

from BaseTest import BaseTest
class FooTestPagesBasic(BaseTest):
    def test_foo(self):
        driver = self.driver
        driver.get(self.base_url + "/")

Pour exécuter, vous spécifiant simplement des tests dans le cadre des arguments de ligne de commande, par exemple:.

./run_tests.py -h http://example.com/ tests/**/*.py
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top