390

In our team, we define most test cases like this:

One "framework" class ourtcfw.py:

import unittest

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

    # Other stuff that we want to use everywhere

And a lot of test cases like 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()

When I'm writing new test code and want to run it often, and save time, I do put "__" in front of all other tests. But it's cumbersome, distracts me from the code I'm writing, and the commit noise this creates is plain annoying.

So, for example, when making changes to testItIsHot(), I want to be able to do this:

$ python testMyCase.py testItIsHot

and have unittest run only testItIsHot()

How can I achieve that?

I tried to rewrite the if __name__ == "__main__": part, but since I'm new to Python, I'm feeling lost and keep bashing into everything else than the methods.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alois Mahdal
  • 10,763
  • 7
  • 51
  • 69

9 Answers9

454

This works as you suggest - you just have to specify the class name as well:

python testMyCase.py MyCase.testItIsHot
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 2
    Oh my! Since the tests are to be run on python2.6 (99% of the time I *can* test the tests themselves with python2.7), I was looking at 2.6.8 doc and missed so much! :-) – Alois Mahdal Apr 12 '13 at 15:16
  • 1
    Just noticed that this works only if the method is called "test*", so unfortunately it cannot be used to occasionally run test that is "disabled" by rename – Alois Mahdal Apr 15 '13 at 12:29
  • 12
    Doesn't work for tests in a subdirectory - the most common case in a mature Python program. – Tom Swirly Apr 24 '15 at 23:23
  • 4
    @TomSwirly Can't check now but I think you can do it by creatiing (empty) `__init__.py` inside that direcrory (and subdirs, if any) and calling eg. `python test/testMyCase.py test.MyCase.testItIsHot`. – Alois Mahdal Oct 22 '15 at 19:27
  • 3
    Nothing happens when I do this. I found workarounds, but I was hoping this method would work for me. – Joe Flack Sep 20 '18 at 15:30
  • This does not work. I get error equivalent of 'module testMyCase has no __path__'. – Anand Feb 01 '19 at 00:33
  • What @JoeFlack said. It doesn’t work when one has a tests/subdirectory – John Greene May 21 '22 at 14:04
214

If you organize your test cases, that is, follow the same organization like the actual code and also use relative imports for modules in the same package, you can also use the following command format:

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

# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Python 3 documentation for this: Command-Line Interface

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ajay M
  • 2,490
  • 1
  • 15
  • 22
  • 12
    This is so clunkily Java-esque. "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ...better hope you didn't modularize into suites like a sane person who tests their code. –  Mar 16 '19 at 02:28
94

It can work well as you guess

python testMyCase.py MyCase.testItIsHot

And there is another way to just test testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)
Community
  • 1
  • 1
Yarkee
  • 9,086
  • 5
  • 28
  • 29
  • 19
    I found the second part of this answer extremely helpful: I am writing tests in Eclipse + PyDev and I don't want to switch to the command line! – Giovanni Di Milia Oct 15 '13 at 17:24
  • To complement this answer, in case you want to run the full TestCase class you can omit the suite part and do instead: `runner = unittest.TextTestRunner()` followed by `runner.run(unittest.makeSuite(MyCase))` – Shlomo Gottlieb Sep 29 '21 at 15:05
34

If you check out the help of the unittest module it tells you about several combinations that allow you to run test case classes from a module and test methods from a test case class.

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
skqr
  • 703
  • 7
  • 8
  • 2
    +1 and since terminology can be confusing if new to a language (and the `usage` is even oddly inconsistent): running `python -m unittest module_test.TestClass.test_method` assumes a file `module_test.py` (run from current directory; and `__init.py__` is _not_ required); and `module_test.py` contains `class TestClass(unittest.TestCase)...` which contains `def test_method(self,...)` (this also works for me on python 2.7.13) – michael Dec 05 '17 at 23:31
22

TL;DR: This would very likely work:

python mypkg/tests/test_module.py MyCase.testItIsHot

The explanation:

  • The convenient way

      python mypkg/tests/test_module.py MyCase.testItIsHot
    

    would work, but its unspoken assumption is you already have this conventional code snippet inside (typically at the end of) your test file.

    if __name__ == "__main__":
        unittest.main()
    
  • The inconvenient way

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

    would always work, without requiring you to have that if __name__ == "__main__": unittest.main() code snippet in your test source file.

So why is the second method considered inconvenient? Because it would be a pain in the <insert one of your body parts here> to type that long, dot-delimited path by hand. While in the first method, the mypkg/tests/test_module.py part can be auto-completed, either by a modern shell, or by your editor.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RayLuo
  • 17,257
  • 6
  • 88
  • 73
20

In case you want to run only tests from a specific class:

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

It works for me in Python 3.6.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bohdan
  • 201
  • 2
  • 2
  • I want to do this but for only one test, but so far I can't get it to work. :( I'm on python 3.8 and it doesn't work even with just doing the whole class, either. – szeitlin Aug 01 '23 at 22:37
  • Disregard earlier comment, it won't let me delete it. :( This works for me in Python 3.8. One thing that surprised me though, if it can't find your file, it won't give you a useful error, it just says "Ran 0 tests", which had me confused for a minute with the wrong path. – szeitlin Aug 01 '23 at 22:44
  • Oh, I'm wrong. It ran all the tests in the file, not just the one class. So it didn't work the way I wanted it to. – szeitlin Aug 01 '23 at 22:59
13

What worked for me was:

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

-v is for unittest verbose log output.

Rodolfo Ortega
  • 397
  • 2
  • 7
10

If you want to run the test directly from a script (for example, from a jupyter notebook), you can do this to run just one test:

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

Note: the -v is optional, only used to get verbosity out of the test.

Another version importing the module:

import testMyCase
unittest.main(argv=['ignored', '-v', 'testMyCase.MyCase.testItIsHot'], exit=False)
ronkov
  • 1,263
  • 9
  • 14
  • 1
    This worked for me. In my case I have the testCase class in the notebook cell so I didn't even need to import it – kiril Jun 07 '23 at 10:00
  • Weird. I thought it worked, but when I checked more carefully, it ran all the tests, not just the one I wanted to select. – szeitlin Aug 01 '23 at 22:59
  • @szeitlin be careful on how do you launch the script. For example, it happened to me that PyCharm by default launches `python unittest C:\your_folder\your_test_file.py`, which results in all tests being launched – ronkov Aug 03 '23 at 08:33
  • 1
    I'm not using pycharm to launch these tests (just to write them!). I eventually got it to work using the TestSuite method, I just don't love that since it's several lines of code to try to run less code :P – szeitlin Aug 03 '23 at 16:41
4

Inspired by yarkee, I combined it with some of the code I already got. You can also call this from another script, just by calling the function run_unit_tests() without requiring to use the command line, or just call it from the command line with python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Sadly this only works for Python 3.3 or above:

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

Runner code:

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

Editing the code a little, you can pass an array with all unit tests you would like to call:

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

    classes = \
    [
        LineBalancingUnitTests,
    ]

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

And another 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 )

Alternatively, you can use load_tests Protocol and define the following method in your test module/file:

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

If you want to limit the execution to one single test file, you just need to set the test discovery pattern to the only file where you defined the load_tests() function.

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

References:

  1. Problem with sys.argv[1] when unittest module is in a script
  2. Is there a way to loop through and execute all of the functions in a Python class?
  3. looping over all member variables of a class in python

Alternatively, to the last main program example, I came up with the following variation after reading the unittest.main() method implementation:

  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() )
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144