Python unittest skip test for only one subclass - python

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.

Related

Mock class instances without calling `__init__` and mock their respective attributes

I have a class MyClass with a complex __init__ function.
This class had a method my_method(self) which I would like to test.
my_method only needs attribute my_attribute from the class instance.
Is there a way I can mock class instances without calling __init__ and by setting the attributes of each class instance instead?
What I have:
# my_class.py
from utils import do_something
class MyClass(object):
def __init__(self, *args, **kwargs):
# complicated function which I would like to bypass when initiating a mocked instance class
pass
def my_method(self):
return do_something(self.my_attribute)
What I tried
#mock.patch("my_class.MyClass")
def test_my_method(class_mock, attribute):
instance = class_mock.return_value
instance.my_attribute = attribute
example_instance = my_class.MyClass()
out_my_method = example_instance.my_method()
# then perform some assertions on `out_my_method`
however this still makes usage of __init__ which I hope we can by-pass or mock.
As I mentioned in the comments, one way to test a single method without having to create an instance is:
MyClass.my_method(any_object_with_my_attribute)
The problem with this, as with both options in quamrana's answer, is that we have now expanded the scope of any future change just because of the tests. If a change to my_method requires access to an additional attribute, we now have to change both the implementation and something else (the SuperClass, the MockMyClass, or in this case any_object_with_my_attribute_and_another_one).
Let's have a more concrete example:
import json
class MyClass:
def __init__(self, filename):
with open(filename) as f:
data = json.load(f)
self.foo = data.foo
self.bar = data.bar
self.baz = data.baz
def my_method(self):
return self.foo ** 2
Here any test that requires an instance of MyClass. is painful because of the file access in __init__. A more testable implementation would split apart the detail of how the data is accessed and the initialisation of a valid instance:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def my_method(self):
return self.foo ** 2
#classmethod
def from_json(cls, filename):
with open(filename) as f:
data = json.load(f)
return cls(data.foo, data.bar, data.baz)
You have to refactor MyClass("path/to/file") to MyClass.from_json("path/to/file"), but wherever you already have the data (e.g. in your tests) you can use e.g. MyClass(1, 2, 3) to create the instance without requiring a file (you only need to consider the file in the tests of from_json itself). This makes it clearer what the instance actually needs, and allows the introduction of other ways to construct an instance without changing the interface.
There are at least two options I can see:
Extract a super class:
class SuperClass:
def __init__(self, attribute):
self.my_attribute = attribute
def my_method(self):
return do_something(self.my_attribute)
class MyClass(SuperClass):
def __init__(self, *args, **kwargs):
super().__init__(attribute) # I don't know where attribute comes from
# complicated function which I would like to bypass when initiating a mocked instance class
Your tests can instantiate SuperClass and call my_method().
Inherit from MyClass as is and make your own simple __init__():
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
Now your test code can instantiate MockMyClass with the required attribute and call my_method()
For instance, you could write the test as follows
def test_my_method(attribute):
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
out_my_method = MockMyClass(attribute).my_method()
# perform assertions on out_my_method

Test abstract class calls parent method in Python

I'm currently refactoring features and I ended up with this abstractions
I have this classes
class AbstractClassA(SomeOtherAbstractClass):
#abstractmethod
def some_abstract_method(self):
pass
def my_method(self)):
service.some_method
class AbstractClassB(AbstractClassA):
#abstractmethod
def another_abstract_method(self):
pass
def some_abstract_method(self):
some_implementation
def my_method(self):
super().my_method()
do_any_other_stuff
And I need to test if the AbstractClassB.my_method calls super().my_method().
I've tried to test this by creating some ImplementationClass that inherits from AbstractClassB and then mocking the AbstractClassA.my_method and checking if it was called but it didn't work...
class AbstractClassBImplementation(AbstractClassB):
def some_abstract_method(self):
calls_service()
class TestAbstractClassB(TestCase):
#patch('module.submodule.AbstractClassA.my_method')
def test_class_b_calls_class_a_my_method(self, my_method_mock):
instance = AbstractClassBImplementation()
instance.my_method()
self.assertTrue(my_method_mock.called)
Someone know how to test this?

Python Mixin - Unresolved Attribute Reference [PyCharm]

I am using a mixin to separate a range of functionality to a different class. This Mixin is only supposed to be mixable with the only child class:
class Mixin:
def complex_operation(self):
return self.foo.capitalize()
class A(Mixin):
def __init__(self):
self.foo = 'foo'
in my method Mixin.complex_operation PyCharm gives warning 'Unresolved Attribute Reference foo'.
Am I using the mixin pattern correctly? Is there a better way? (I would like to have type hints and autocompletion in my mixins, and I would like to have multiple mixins.)
Declare the necessary fields in the Mixin like:
class Mixin:
foo: str
def complex_operation(self):
return self.foo.capitalize()
This way the mixin actually declares the fields a class must have to be able to use this mixin. Type hint will create warnings if extending class will put incompatible type into declared field.
edit: Replaced foo = None with foo:str as suggested by #valex
I see few options.
1) Type annotations (i think this is cleanest solution):
class Mixin:
foo: str
def complex_operation(self):
return self.foo.capitalize()
2) Default None (#ikamen option):
class Mixin:
foo = None
def complex_operation(self):
return self.foo.capitalize()
3) Suppress unresolved reference error for class or for specific line (i think this is more dirty way than first two):
# noinspection PyUnresolvedReferences
class Mixin:
def complex_operation(self):
return self.foo.capitalize()
class Mixin:
def complex_operation(self):
# noinspection PyUnresolvedReferences
return self.foo.capitalize()
So just to compiling my thoughts from the comments for everyone else:
The problem is keeping the two classes intrinsically connected while separating functionality. Here are my solutions:
1) Make a module
Have another file, say mixin.py, that has complex_operation as a function. Instead of accepting self as a parameter, have it take a string:
# mixin.py
def complex_operation (foo: str) -> str: return foo.capitalize()
# main.py
from ai import complex_operation
class A:
def __init__(self): self.foo = "foo"
print (complex_operation (A().foo))
2) Make a class to accept another class as a parameter
In Mixin's __init__ function, add a parameter to accept an A, and then use that in its methods:
# mixin.py
class Mixin:
def __init__(self, a: A): self.a = a
def complex_operation(self): return self.a.foo.capitalize()
# main.py
from mixin import Mixin
class A:
def __init__(self): self.foo = "foo"
print (Mixin (A()).complex_operation())

Add simple test method to TestSuite

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.

Using a class decorator, how to override a method without redefining the class?

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):
...

Categories

Resources