Add simple test method to TestSuite - python

How can I add simple test method from unittest.TestCase to TestSuite. As I see it is only possible to add whole class only to suite, for example I want something like this:
import unittest
class MyBaseTestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.abs = "test"
class MyTestClass(MyBaseTestCase):
def test_abs(self):
if self.abs:
pass
class MyTestSuite(unittest.TestSuite):
def __init__(self):
super().__init__()
self.addTest(MyTestClass.test_abs)
Here I get an error: AttributeError: 'TeamcityTestResult' object has no attribute 'abs'. It seems like it runs as a test, but setUpClass does not calls.

How did you run the test suite? I used your code and ran it using 'python3 -m unittest test.py':
import unittest
class MyBaseTestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.abs = "test"
class MyTestClass(MyBaseTestCase):
def test_abs(self):
if self.abs:
pass
class MyTestSuite(unittest.TestSuite):
def __init__(self):
super().__init__()
self.addTest(MyTestClass.test_abs)
And it works.

Related

Python unit test with mocking assert_called for function outside class

I am trying to test whether a function defined outside of a class is called by a class method.
def function_a(input):
return input
class MyClass:
def __init__(self, user_function=function_a):
self.user_function = user_function
def function_b(input):
return self.user_function(input)
I am trying to test the function call like this:
from unittest import TestCase
from unittest.mock import Mock
class TestMyClass(TestCase):
def test_function_call(self):
my_class = MyClass(user_function=function_a)
mock = Mock()
my_class.function_b(input)
mock.function_a.assert_called()
This gives me
AssertionError: Expected 'function_a' to have been called.
I am using Python 3.6. Looking at the output I can see that function_a was called. What am I doing wrong?
This here did the trick:
from unittest import TestCase
from unittest.mock import MagicMock
class TestMyClass(TestCase):
def test_function_call(self):
my_class = MyClass(user_function=MagicMock())
my_class.function_b(input)
my_class.user_function.assert_called()

How can I import a testclass properly to inherit from, without it being run as a test

Context
I have a test class where all my tests inherit from. It cant run by itself as it really doesnt contain any setup info
I wanted to add a test which is executed by ALL tests (adding it to the baseclass seems logical)
But now I notice the basetestclass( => Foo) which I import is being detected as a test itself and runs and is visible in the reports
Code
the base class in base.py
from unittest import TestCase
class Foo(TestCase):
#classmethod
def setUpClass(cls):
# prepare the generic setup stuff based on what is defined in the child class
print("setupclass Foo done")
def test_run_in_all_inherited_tests(self):
print("fooBar")
assert True
the real test in test_something.py
from base import Foo # <= This is being detected as a testclass object and thus will be executed
class TestFoo(Foo):
#classmethod
def setUpClass(cls):
# define specific test setup
super().setUpClass()
print("setup TestFoo done")
def test_pass(self):
pass
def test_assert(self):
assert False
This triggers a testrun of the imported Foo
The Question
How can I import Foo without that its being detected as a 'test'
If I remove the test to run in all tests all is fine.
Adding #nottest decorator to Foo wont work since then also all inherited classes are defined nottest.
It needs to run on nose, pytest and unittest testrunners
I noticed if I changed the import statement like below that it also works. But that would mean adjusting a few hundreds of testfiles in different repos. (I'd like to avoid that)
import base
class TestFoo(base.Foo):
The key to the answer seems to be that each test has an attribute __test__ which is set to True when it is a test.
Setting it to False when the class should not be a test will then let the test collector ignore this class.
The answer assumes I can only do changes in the base.py
In python 3.9 classmethod and property decorators can be combined so I wrote a separate answer for that
answer for < py3.9
the base class in base.py
from unittest import TestCase
class MetaFoo(type):
#property
def __test__(cls):
return cls != Foo
class Foo(TestCase, metaclass=MetaFoo):
#classmethod
def setUpClass(cls):
# prepare the generic setup stuff based on what is defined in the child class
print("setupclass Foo done")
def test_run_in_all_inherited_tests(self):
print("fooBar")
assert True
answer for >= py3.9
the base class in base.py
from unittest import TestCase
class Foo(TestCase):
#classmethod
#property
def __test__(cls):
return cls != Foo
#classmethod
def setUpClass(cls):
# prepare the generic setup stuff based on what is defined in the child class
print("setupclass Foo done")
def test_run_in_all_inherited_tests(self):
print("fooBar")
assert True
the actual test
test_something.py
from base import Foo # <= This will not be detected as a test anymore as __test__ returns False
class TestFoo(Foo):
#classmethod
def setUpClass(cls):
# define specific test setup
super().setUpClass()
print("setup TestFoo done")
def test_pass(self):
pass
def test_assert(self):
assert False
This doesnt trigger a testrun of the imported Foo anymore

Python unittest skip test for only one subclass

Let's say I have my unittest set up like this:
import unittest
class BaseTest(object):
def setup(self):
self.foo = None
def test_something(self):
self.assertTrue(self.foo.something())
def test_another(self):
self.assertTrue(self.foo.another())
def test_a_third_thing(self):
self.assertTrue(self.foo.a_third_thing())
class TestA(BaseTest, unittest.TestCase):
def setup(self):
self.foo = FooA()
class TestB(BaseTest, unittest.TestCase):
def setup(self):
self.foo = FooB()
class TestC(BaseTest, unittest.TestCase):
def setup(self):
self.foo = FooC()
Now let's say FooC doesn't have a_third_thing implemented yet, and I want to skip test_a_third_thing for ONLY the TestC class. Is there some way I can use the #unittest.skipif decorator to do this? Or some other handy way to skip this test for only this class?
Python 2.7, in case it matters
You may not need to "skip" the test. One simple approach is to override the base test with a dummy.
class TestC(BaseTest, unittest.TestCase):
def setup(self):
self.foo = FooC()
def test_a_third_thing(self):
"""Override the assertions of the base test."""
pass
You cannot use #unittest.skipif here because it is evaluated during module, and the check needed should be run during runtime.
To achieve desired result your test_a_third_thing in base class should look like this:
class BaseTest(unittest.TestCase):
def test_a_third_thing(self):
if not getattr(self.foo, "a_third_thing", None):
self.skipTest(self.foo.__class__.__name__ + ' has no a_third_thing, skip')
else:
self.assertTrue(self.foo.a_third_thing())
Also fix typos in your example setup to setUp. Remove 'unittest.TestCase' from inheritance list of test classes and add to base class.

Django test global setup

I have some file for unit test with django:
test1.py
class Test1(unittest.TestCase):
def setUp(self):
...
def tearDown(self):
...
test1.py
class Test1(unittest.TestCase):
def setUp(self):
...
def tearDown(self):
...
testn.py
class Testn(unittest.TestCase):
def setUp(self):
...
def tearDown(self):
...
I want to create a global setup to make some configuration for it all test, someting like:
some_file.py
class GlobalSetUpTest(SomeClass):
def setup(self): # or any function name
global_stuff = "whatever"
is that possible? if so, how? Thanks in advance.
You could just create a parent class with your custom global setUp method and then have all of your other test classes extend that:
class MyTestCase(unittest.TestCase):
def setUp(self):
self.global_stuff = "whatever"
class TestOne(MyTestCase):
def test_one(self):
a = self.global_stuff
class TestTwo(MyTestCase):
def setUp(self):
# Other setUp operations here
super(TestTwo, self).setUp() # this will call MyTestCase.setUp to ensure self.global_stuff is assigned.
def test_two(self):
a = self.global_stuff
Obviously you could use the same technique for a 'global' tearDown method.
If you want to have it only run once for all tests, you can override the test management command by placing a management/commands/test.py in one of your apps:
from django.core.management.commands import test
class Command(test.Command):
def handle(self, *args, **options):
# Do your magic here
super(Command, self).handle(*args, **options)
Unfortunately this does not work well with PyCharm.
In PyCharm you can use the "Before Lunch" Task instead.

Python unittest - setUpClass() is giving me trouble - why can't I inherit like this?

I have unittest code like the following:
import unittest
class MyUnitTest(unittest.TestCase):
def setUpClass(self):
do_something_expensive_for_all_sets_of_tests()
class MyFirstSetOfTests(MyUnitTest):
def setUpClass(self):
super(MyFirstSetOfTests, self).setUpClass()
do_something_expensive_for_just_these_first_tests()
def test_one(self):
...
def test_two(self):
...
class MySecondSetOfTests(MyUnitTest):
def setUpClass(self):
super(MySecondSetOfTests, self).setUpClass()
do_something_expensive_for_just_these_second_tests()
def test_one(self):
...
def test_two(self):
...
if __name__ == '__main__':
unittest.main()
When I try to run this code, I get an error like this:
======================================================================
ERROR: setUpClass (__main__.MyFirstSetOfTests)
----------------------------------------------------------------------
TypeError: unbound method setUpClass() must be called with MyFirstSetOfTests instance as first argument (got nothing instead)
----------------------------------------------------------------------
setUpClass must be a class method. From the documentation:
A class method called before tests in an individual class run. setUpClass is called with the class as the only argument and must be decorated as a classmethod():
#classmethod
def setUpClass(cls):
...
See Class and Module Fixtures for more details.
Your version is missing the #classmethod decorator:
class MyUnitTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
do_something_expensive_for_all_sets_of_tests()
class MyFirstSetOfTests(MyUnitTest):
#classmethod
def setUpClass(cls):
super(MyFirstSetOfTests, cls).setUpClass()
do_something_expensive_for_just_these_first_tests()
The error is thrown because MyFirstSetOfTests.setUpClass() is called on the class, not on an instance, but you didn't mark your method as a classmethod and thus it was not passed in the automatic self argument. In the above updated code I used cls instead, to reflect that the name references the class object.

Categories

Resources