When running a simple unittest it would sometimes be easier to be able to keep the tests inside the class. However, I don't know how to reload the current module, and so whenever that's needed I have to move the tests into a separate module. Is there a way around this?
module: foo
import unittest
class MyObject
...
class MockMyObject
...
class TestMock(unittest.TestCase):
def setUp(self):
MyObject = MockMyObject
mocked = MyObject()
def tearDown(self):
reload(foo) # what goes here?
def testFunction(self):
mocked.do_mocked_function()
if __name__ == "__main__":
unittest.main()
The way I've found to handle this is to import sys and reload(sys.modules[__name__]) in the tearDown method, but I'm wondering if there is a better method.
You can save your original class in a variable and restore it in the tearDown function.
Here is an example:
class TestMock(unittest.TestCase):
original = MyObject
def setUp(self):
global MyObject
MyObject = MockMyObject
def tearDown(self):
global MyObject
MyObject = TestMock.original
def testFunction(self):
MyObject().do_mocked_function()
that's not a good idea to reload your module.
class TestMock(unittest.TestCase):
def setUp(self):
MyObject = MockMyObject
self.mocked = MyObject()
def tearDown(self):
pass
def testFunction(self):
self.mocked.do_mocked_function()
Related
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
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.
I use py.test for testing.
In setup_class() I need to use tmpdir for my class constructor:
class TestMyClass:
def setup_class(self):
self.t = MyClass(path=tmpdir)
def test_test(self):
assert True
And I have an error:
NameError: name 'tmpdir' is not defined
I can't use setup_class(self, tmpdir).
If I use this code:
def test_needsfiles(tmpdir):
print(tmpdir)
assert 0
It's work, but I need tmpdir in my class constructor.
How to do this?
Thanks!
UPD
I try to do this:
#pytest.yield_fixture()
def constructor(tmpdir):
_t = MyClass(path=str(tmpdir))
yield _t
class TestMyClass:
def test_test(self, constructor):
pass
But I can't use scopes in fixture:
ScopeMismatch: You tried to access the 'function' scoped fixture 'tmpdir' with a 'module' scoped request object, involved factories
I do this:
class TestMyClass:
#pytest.fixture(autouse=True)
def setup(self, tmpdir):
self.tmpdir = tmpdir.strpath
If you don't want to use #pytest.fixture(autouse=True) as #santon suggested, but you want to create a fixture outside TestMyClass (like you write in the UPD part), you can try this:
#pytest.fixture
def t(tmpdir):
return MyClass(tmpdir)
class TestMyClass:
def test_test(self, t):
assert True
If you don't want to return anything in a fixture, but for example go to a temp directory, you can also do:
#pytest.fixture
def t(tmpdir):
os.chdir(str(tmpdir))
#pytest.mark.usefixtures("t")
class TestMyClass:
def test_test(self):
assert True
You can use tempfile module to handle with temporary files and dirs. In setup you can create temp dir with mkdtemp and delete it at tearDown from test class.
import shutil, tempfile
import unittest
class TestMyClass(unittest.TestCase):
def setUp(self):
self.tmp_dir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tmp_dir)
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.
For unit tests (using the unittest module) that use the App Engine testbed, I need setUp and tearDown methods to activate and deactivate the testbed, respectively (slightly simplified):
class SomeTest(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
def tearDown(self):
self.testbed.deactivate()
def testSomething(self):
...
This quickly becomes a burden to write. I could write a base class TestCaseWithTestbed, but then I'd have to remember to call the superclass method each time I need a custom setUp in one of the test cases.
I thought it would be more elegant to solve this with a class decorator instead. So I'd like to write:
#WithTestbed
class SomeTest(unittest.TestCase):
def testSomething(self):
...
With this decorator applied, the testbed should just be activated magically. So... how to implement the WithTestbed decorator? I currently have the following:
def WithTestbed(cls):
class ClsWithTestbed(cls):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
cls.setUp(self)
def tearDown(self):
cls.tearDown(self)
self.testbed.deactivate()
return ClsWithTestbed
This works for simple cases, but has some serious problems:
The name of the test class becomes ClsWithTestbed and this shows up in the test output.
Concrete test classes calling super(SomeTestClass, self).setUp() end up in an infinite recursion, because SomeTestClass is now equal to WithTestbed.
I'm a bit hazy on Python's runtime type manipulation. So, how to do this the Right Way?
This appears to work and solve the problems:
def WithTestbed(cls):
def DoNothing(self):
pass
orig_setUp = getattr(cls, 'setUp', DoNothing)
orig_tearDown = getattr(cls, 'tearDown', DoNothing)
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
orig_setUp(self)
def tearDown(self):
orig_tearDown(self)
self.testbed.deactivate()
cls.setUp = setUp
cls.tearDown = tearDown
return cls
Does anyone see any problems with this approach?
Here's a simple way to do what you're asking with subclassing instead of a decorator:
class TestCaseWithTestBed(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.mySetUp()
def tearDown(self):
self.myTearDown()
self.testbed.deactivate()
def mySetUp(self): pass
def myTearDown(self): pass
class SomeTest(TestCaseWithTestBed):
def mySetUp(self):
"Insert custom setup here"
All you have to do is define mySetUp and myTearDown in your test cases instead of setUp and tearDown.
Something like this would work:
def WithTestbed(cls):
cls._post_testbed_setUp = getattr(cls, 'setUp', lambda self : None)
cls._post_testbed_tearDown = getattr(cls, 'tearDown', lambda self : None)
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self._post_testbed_setUp()
def tearDown(self):
self.testbed.deactivate()
self._post_testbed_tearDown()
cls.setUp = setUp
cls.tearDown = tearDown
return cls
#WithTestbed
class SomeTest(object):
...