Esecuzione di test singoli da unittest.TestCase tramite riga di comando


257

Nel nostro team, definiamo la maggior parte dei casi di test come questo:

Una classe "quadro" ourtcfw.py:

import unittest

class OurTcFw(unittest.TestCase):
    def setUp:
        # something

    # other stuff that we want to use everywhere

e molti casi di test come testMyCase.py:

import localweather

class MyCase(OurTcFw):

    def testItIsSunny(self):
        self.assertTrue(localweather.sunny)

    def testItIsHot(self):
        self.assertTrue(localweather.temperature > 20)

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

Quando scrivo un nuovo codice di test e voglio eseguirlo spesso e risparmiare tempo, quello che faccio è che metto "__" davanti a tutti gli altri test. Ma è ingombrante, mi distoglie dal codice che sto scrivendo e il rumore commesso che questo crea è chiaramente fastidioso.

Ad esempio, quando apporto modifiche a testItIsHot(), voglio essere in grado di farlo:

$ python testMyCase.py testItIsHot

e ho unittesteseguito solo testItIsHot()

Come posso raggiungerlo?

Ho provato a riscrivere la if __name__ == "__main__":parte, ma dato che sono nuovo in Python, mi sento perso e continuo a crogiolarmi in tutto il resto rispetto ai metodi.

Risposte:


311

Funziona come suggerisci: devi solo specificare anche il nome della classe:

python testMyCase.py MyCase.testItIsHot

2
Oh mio! Dato che i test devono essere eseguiti su python2.6 (il 99% delle volte posso testare i test stessi con python2.7), stavo guardando 2.6.8 doc e mi sono perso così tanto! :-)
Alois Mahdal

1
Ho appena notato che questo funziona solo se il metodo si chiama "test *", quindi sfortunatamente non può essere usato per eseguire occasionalmente test che sono "disabilitati" rinominando
Alois Mahdal

4
Non funziona per i test in una sottodirectory: il caso più comune in un programma Python maturo.
Tom Swirly,

4
@TomSwirly Non riesco a controllare ora, ma penso che tu possa farlo creando (vuoto) __init__.pyall'interno di quella directory (e sottodirizzi, se presente) e chiamando ad es. python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal,

1
Non succede nulla quando lo faccio. Ho trovato soluzioni alternative, ma speravo che questo metodo avrebbe funzionato per me.
Joe Flack,

152

Se organizzi i casi di test, ovvero segui la stessa organizzazione come il codice effettivo e usi anche le importazioni relative per i moduli nello stesso pacchetto

Puoi anche usare il seguente formato di comando:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Documentazione Python3 per questo: https://docs.python.org/3/library/unittest.html#command-line-interface


Questo è così clunkily Java-esque. "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ... meglio sperare di non aver modularizzato in suite come una persona sana che prova il loro codice.
Joshua Detwiler,

69

Può funzionare bene come indovini

python testMyCase.py MyCase.testItIsHot

E c'è un altro modo per testare testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)

11
Ho trovato estremamente utile la seconda parte di questa risposta: sto scrivendo dei test in Eclipse + PyDev e non voglio passare alla riga di comando!
Giovanni Di Milia,

25

Se si controlla l'aiuto del modulo unittest, vengono descritte diverse combinazioni che consentono di eseguire classi di test case da un modulo e metodi di test da una classe di test case.

python3 -m unittest -h

[...]

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method

Non richiede di definire a unittest.main()come comportamento predefinito del modulo.


2
+1 e poiché la terminologia può essere fonte di confusione se non si conosce una lingua (e il fatto usageè stranamente incoerente): l'esecuzione python -m unittest module_test.TestClass.test_methodpresuppone un file module_test.py(eseguito dalla directory corrente; e non__init.py__ è necessario); e contiene quale contiene (questo funziona anche per me su Python 2.7.13)module_test.pyclass TestClass(unittest.TestCase)...def test_method(self,...)
michael

11

Forse, sarà utile per qualcuno. Nel caso in cui si desideri eseguire solo test di una classe specifica:

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

Funziona per me in Python 3.6


3

Ispirato da @yarkee, l' ho combinato con alcuni dei codici che ho già ottenuto. Puoi anche chiamarlo da un altro script, semplicemente chiamando la funzione run_unit_tests()senza la necessità di utilizzare la riga di comando, o semplicemente chiamandolo dalla riga di comando con python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Purtroppo questo funziona solo per Python 3.3o superiore:

import unittest

class LineBalancingUnitTests(unittest.TestCase):

    @classmethod
    def setUp(self):
        self.maxDiff = None

    def test_it_is_sunny(self):
        self.assertTrue("a" == "a")

    def test_it_is_hot(self):
        self.assertTrue("a" != "b")

Codice del corridore:

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from .somewhere import LineBalancingUnitTests

def create_suite(classes, unit_tests_to_run):
    suite = unittest.TestSuite()
    unit_tests_to_run_count = len( unit_tests_to_run )

    for _class in classes:
        _object = _class()
        for function_name in dir( _object ):
            if function_name.lower().startswith( "test" ):
                if unit_tests_to_run_count > 0 \
                        and function_name not in unit_tests_to_run:
                    continue
                suite.addTest( _class( function_name ) )
    return suite

def run_unit_tests():
    runner = unittest.TextTestRunner()
    classes =  [
        LineBalancingUnitTests,
    ]

    # Comment all the tests names on this list, to run all Unit Tests
    unit_tests_to_run =  [
        "test_it_is_sunny",
        # "test_it_is_hot",
    ]
    runner.run( create_suite( classes, unit_tests_to_run ) )

if __name__ == "__main__":
    print( "\n\n" )
    run_unit_tests()

Modificando un po 'il codice, è possibile passare un array con tutti i test unitari che si desidera chiamare:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

    runner.run( suite( classes, unit_tests_to_run ) )
...

E un altro file:

import my_test_file

# Comment all the tests names on this list, to run all Unit Tests
unit_tests_to_run = \
[
    "test_it_is_sunny",
    # "test_it_is_hot",
]

my_test_file.run_unit_tests( unit_tests_to_run )

In alternativa, puoi utilizzare https://docs.python.org/3/library/unittest.html#load-tests-protocol e definire il seguente metodo sul tuo modulo / file di test:

def load_tests(loader, standard_tests, pattern):
    suite = unittest.TestSuite()

    # To add a single test from this file
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )

    # To add a single test class from this file
    suite.addTests( unittest.TestLoader().loadTestsFromTestCase( LineBalancingUnitTests ) )

    return suite

Se si desidera limitare l'esecuzione a un singolo file di test, è sufficiente impostare il modello di rilevamento test sull'unico file in cui è stata definita la load_tests()funzione.

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest

test_pattern = 'mytest/module/name.py'
PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )

loader = unittest.TestLoader()
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

suite = loader.discover( start_dir, test_pattern )
runner = unittest.TextTestRunner( verbosity=2 )
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )

sys.exit( not results.wasSuccessful() )

Riferimenti:

  1. Problema con sys.argv [1] quando il modulo unittest si trova in uno script
  2. C'è un modo per scorrere ed eseguire tutte le funzioni in una classe Python?
  3. eseguire il ciclo su tutte le variabili membro di una classe in Python

In alternativa all'ultimo esempio del programma principale, ho trovato la seguente variazione dopo aver letto l' unittest.main()implementazione del metodo:

  1. https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L65
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import unittest

PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

from testing_package import main_unit_tests_module
testNames = ["TestCaseClassName.test_nameHelloWorld"]

loader = unittest.TestLoader()
suite = loader.loadTestsFromNames( testNames, main_unit_tests_module )

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

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )

3

TL; DR : molto probabilmente funzionerebbe:

python mypkg/tests/test_module.py MyCase.testItIsHot

La spiegazione :

  • Il modo conveniente

    python mypkg/tests/test_module.py MyCase.testItIsHot

    funzionerebbe MA il suo presupposto non detto è che hai già questo frammento di codice convenzionale all'interno (in genere alla fine di) il tuo file di test.

    if __name__ == "__main__":
        unittest.main()
  • Il modo inopportuno

    python -m unittest mypkg.tests.test_module.TestClass.test_method

    funzionerebbe sempre, senza richiedere lo if __name__ == "__main__": unittest.main()snippet di codice nel file di origine del test.

Quindi perché il secondo metodo è considerato scomodo? Perché sarebbe un dolore nel (_ inserire una parte del tuo corpo qui _) digitare a mano quel lungo percorso delimitato da punti. Mentre nel 1o metodo, la mypkg/tests/test_module.pyparte può essere completata automaticamente, sia da una shell moderna, sia dall'editor.

PS: Se pensavi che la parte del corpo fosse da qualche parte sotto la vita, sei una persona autentica. :-) Intendo dire "articolazione delle dita". Troppa digitazione sarebbe dannosa per le articolazioni. ;-)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.