Uruchamianie pojedynczego testu z unittest.TestCase za pomocą wiersza poleceń


256

W naszym zespole definiujemy większość takich przypadków testowych:

Jedna klasa „framework” ourtcfw.py:

import unittest

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

    # other stuff that we want to use everywhere

i wiele przypadków testowych, takich jak 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()

Kiedy piszę nowy kod testowy i chcę go często uruchamiać oraz oszczędzać czas, robię to, że przed wszystkimi innymi testami umieszczam „__”. Ale jest to uciążliwe, odciąga mnie od kodu, który piszę, a generowany przez niego szum zatwierdzania jest po prostu denerwujący.

Tak więc np. Kiedy wprowadzam zmiany testItIsHot(), chcę móc to zrobić:

$ python testMyCase.py testItIsHot

i unittestuciekłem tylko testItIsHot()

Jak mogę to osiągnąć?

Próbowałem przepisać tę if __name__ == "__main__":część, ale ponieważ jestem nowy w Pythonie, czuję się zagubiony i wciąż wdaję się we wszystko inne niż metody.

Odpowiedzi:


311

Działa to tak, jak sugerujesz - musisz również podać nazwę klasy:

python testMyCase.py MyCase.testItIsHot

2
O mój! Ponieważ testy mają być uruchamiane na python2.6 (99% czasu I można przetestować sami z python2.7 testy), szukałem na 2.6.8 doc i nieodebranych tak wiele! :-)
Alois Mahdal

1
Zauważyłem, że działa to tylko wtedy, gdy metoda nazywa się „test *”, więc niestety nie można jej używać do sporadycznego uruchamiania testu „wyłączonego” przez zmianę nazwy
Alois Mahdal

4
Nie działa w przypadku testów w podkatalogu - najczęstszy przypadek w dojrzałym programie w języku Python.
Tom Swirly,

4
@TomSwirly Nie mogę teraz sprawdzić, ale myślę, że możesz to zrobić, tworząc (pusty) __init__.pywewnątrz tego katalogu (i podkatalogów, jeśli istnieją) i wywołując np. python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal

1
Kiedy to robię, nic się nie dzieje. Znalazłem obejścia, ale miałem nadzieję, że ta metoda zadziała dla mnie.
Joe Flack,

152

Jeśli organizujesz przypadki testowe, to znaczy postępuj zgodnie z tą samą organizacją, co rzeczywisty kod, a także korzystaj z importu względnego dla modułów w tym samym pakiecie

Możesz także użyć następującego formatu poleceń:

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

Dokumentacja Python3 dotycząca tego: https://docs.python.org/3/library/unittest.html#command-line-interface


To jest tak niezgrabnie jak Java. „long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention” ... lepiej mieć nadzieję, że nie zmodularyzowałeś się w pakiety jak normalny człowiek, który testuje swój kod.
Joshua Detwiler

69

Może działać dobrze, jak się domyślacie

python testMyCase.py MyCase.testItIsHot

Jest też inny sposób na przetestowanie testItIsHot:

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

11
Uważam, że druga część tej odpowiedzi jest bardzo pomocna: piszę testy w Eclipse + PyDev i nie chcę przełączać się do wiersza poleceń!
Giovanni Di Milia

25

Jeśli sprawdzisz pomoc najczystszego modułu, dowiesz się o kilku kombinacjach, które pozwalają na uruchamianie klas przypadków testowych z modułu i metod testowych z klasy przypadków testowych.

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

Nie wymaga definiowania unittest.main()domyślnego zachowania modułu.


2
+1, a ponieważ terminologia może być myląca, jeśli jest nowa w języku (a usagejest nawet dziwnie niespójna): uruchamianie python -m unittest module_test.TestClass.test_methodzakłada plik module_test.py(uruchamiany z bieżącego katalogu; i nie__init.py__ jest wymagany); i zawiera, który zawiera (działa to również dla mnie w Pythonie 2.7.13)module_test.pyclass TestClass(unittest.TestCase)...def test_method(self,...)
Michael

10

Może komuś się przyda. Jeśli chcesz uruchamiać tylko testy z określonej klasy:

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

Działa dla mnie w Pythonie 3.6


3

Zainspirowany przez @yarkee połączyłem go z częścią kodu, który już dostałem. Możesz także wywołać to z innego skryptu, po prostu wywołując funkcję run_unit_tests()bez konieczności używania wiersza poleceń lub po prostu wywołując go z wiersza poleceń za pomocą python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Niestety działa to tylko dla Python 3.3lub przełożonego:

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")

Kod biegacza:

#! /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()

Trochę edytując kod, możesz przekazać tablicę ze wszystkimi testami jednostkowymi, które chcesz wywołać:

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

    classes = \
    [
        LineBalancingUnitTests,
    ]

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

I inny plik:

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 )

Alternatywnie możesz użyć https://docs.python.org/3/library/unittest.html#load-tests-protocol i zdefiniować następującą metodę w module / pliku testowym:

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

Jeśli chcesz ograniczyć wykonanie do jednego pliku testowego, wystarczy ustawić wzorzec wykrywania testu na jedyny plik, w którym zdefiniowano load_tests()funkcję.

#! /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() )

Bibliografia:

  1. Problem z sys.argv [1], gdy niezabezpieczony moduł znajduje się w skrypcie
  2. Czy istnieje sposób na zapętlenie i wykonanie wszystkich funkcji w klasie Python?
  3. zapętlanie wszystkich zmiennych składowych klasy w pythonie

Alternatywnie do ostatniego przykładu programu głównego po przeczytaniu unittest.main()implementacji metody wpadłem na następującą odmianę :

  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 : To prawdopodobnie działałoby:

python mypkg/tests/test_module.py MyCase.testItIsHot

Wyjaśnienie :

  • Wygodny sposób

    python mypkg/tests/test_module.py MyCase.testItIsHot

    działałoby ALE jego niewypowiedzianym założeniem jest to, że masz już ten konwencjonalny fragment kodu w (zwykle na końcu) pliku testowym.

    if __name__ == "__main__":
        unittest.main()
  • Niewygodny sposób

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

    zawsze będzie działać, bez konieczności posiadania tego if __name__ == "__main__": unittest.main()fragmentu kodu w testowym pliku źródłowym.

Dlaczego więc druga metoda jest uważana za niewygodną? Ponieważ trudno byłoby ( ręcznie wstawić tutaj jedną z części ciała _) wpisać tę długą ścieżkę oddzieloną kropkami. Podczas gdy w 1. metodzie, mypkg/tests/test_module.pyczęść może być automatycznie uzupełniana, albo przez nowoczesną powłokę, albo przez edytora.

PS: Jeśli uważasz, że ta część ciała znajduje się gdzieś poniżej talii, jesteś autentyczną osobą. :-) Mam na myśli „staw palcowy”. Zbyt dużo pisania byłoby niekorzystne dla twoich stawów. ;-)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.