Een enkele test uitvoeren vanuit unittest.TestCase via de opdrachtregel

In ons team definiëren we de meeste testgevallen als volgt:

Eén “framework”-klasse ourtcfw.py:

import unittest
class OurTcFw(unittest.TestCase):
    def setUp:
        # Something
    # Other stuff that we want to use everywhere

En veel testgevallen zoals 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()

Als ik nieuwe testcode schrijf en deze vaak wil uitvoeren en tijd wil besparen, plaats ik “__” voor alle andere tests. Maar het is omslachtig, het leidt me af van de code die ik aan het schrijven ben, en de commit-ruis die dit veroorzaakt is ronduit vervelend.

Dus als ik bijvoorbeeld wijzigingen aanbreng in testItIsHot(), wil ik dit kunnen doen:

$ python testMyCase.py testItIsHot

en unittestalleentestItIsHot()

laten uitvoeren

Hoe kan ik dat bereiken?

Ik heb geprobeerd het gedeelte if __name__ == "__main__":te herschrijven, maar aangezien ik nieuw ben in Python, voel ik me verloren en blijf ik in alles behalve de methoden bashen.


Antwoord 1, autoriteit 100%

Dit werkt zoals je voorstelt – je hoeft alleen maar de klassenaam op te geven:

python testMyCase.py MyCase.testItIsHot

Antwoord 2, autoriteit 47%

Als u uw testgevallen organiseert, dat wil zeggen, dezelfde organisatie volgt als de eigenlijke code en ook relatieve imports gebruikt voor modules in hetzelfde pakket, kunt u ook de volgende opdrachtindeling gebruiken:

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

Python 3-documentatie hiervoor: Command- Lijninterface


Antwoord 3, autoriteit 22%

Het kan goed werken zoals u vermoedt

python testMyCase.py MyCase.testItIsHot

En er is een andere manier om gewoon testItIsHotte testen:

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

Antwoord 4, autoriteit 8%

Als je de hulp van de unittest-module bekijkt, vertelt deze je over verschillende combinaties waarmee je testcaseklassen uit een module en testmethoden uit een testcaseklasse kunt draaien.

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
```lang-none
It does not require you to define a `unittest.main()` as the default behaviour of your module.

Antwoord 5, autoriteit 4%

In het geval u alleen tests van een specifieke klasse wilt uitvoeren:

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

Het werkt voor mij in Python 3.6.


Antwoord 6, autoriteit 4%

TL;DR: Dit zou zeer waarschijnlijk werken:

python mypkg/tests/test_module.py MyCase.testItIsHot

De uitleg:

  • De gemakkelijke manier

     python mypkg/tests/test_module.py MyCase.testItIsHot
    

    zou werken, maarde onuitgesproken veronderstelling is dat je dit conventionele codefragment al in (meestal aan het einde van) je testbestand hebt staan.

    if __name__ == "__main__":
        unittest.main()
    
  • De onhandige manier

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

    zou altijd werken, zonder dat u dat if __name__ == "__main__": unittest.main()codefragment in uw testbronbestand hoeft te hebben.

Dus waarom wordt de tweede methode als onhandig beschouwd? Omdat het vervelend zou zijn om <voeg hier een van uw lichaamsdelen in> om dat lange, door punten gescheiden pad met de hand te typen. Bij de eerste methode kan het deel mypkg/tests/test_module.pyautomatisch worden aangevuld, hetzij door een moderne shell, hetzij door uw editor.


Antwoord 7

Geïnspireerd door yarkee , ik heb het gecombineerd met een deel van de code die ik al heb. Je kunt dit ook vanuit een ander script aanroepen, gewoon door de functie run_unit_tests()aan te roepen zonder dat je de opdrachtregel hoeft te gebruiken, of door het gewoon vanaf de opdrachtregel aan te roepen met python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Helaas werkt dit alleen voor Python 3.3 of hoger:

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

Runnercode:

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

Als je de code een beetje aanpast, kun je een array doorstaan ​​met alle eenheidstests die je wilt aanroepen:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()
    classes = \
    [
        LineBalancingUnitTests,
    ]
    runner.run( suite( classes, unit_tests_to_run ) )
...

En nog een bestand:

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 )

Als alternatief kunt u load_tests Protocolen definieer de volgende methode in uw testmodule/bestand:

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

Als u de uitvoering wilt beperken tot één enkel testbestand, hoeft u alleen het testdetectiepatroon in te stellen op het enige bestand waarin u de functie load_tests()hebt gedefinieerd.

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

Referenties:

  1. Probleem met sys.argv[1] wanneer unittest-module in een script zit
  2. Is er een manier om alle functies in een Python-klasse te doorlopen en uit te voeren?
  3. looping over alle lidvariabelen van een les in python

Als alternatief kwam ik op het laatste voorbeeld van het hoofdprogramma met de volgende variatie na het lezen van de unittest.main()methode-implementatie:

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

Antwoord 8

Als u de test rechtstreeks vanuit een script wilt uitvoeren (bijvoorbeeld vanuit een jupyter-notebook), kunt u dit doen om slechts één test uit te voeren:

from testMyCase import MyCase
unittest.main(argv=['ignored', '-v', 'MyCase.testItIsHot'], exit=False)

Antwoord 9

Wat voor mij werkte was:

cd project_dir
python -m unittest -v path\to\test\testMyCase.py -k my_test_name

-v is voor uitgebreide log-uitvoer van unittest.

Other episodes