Are unittest attributes mutable in Python? - python

Suppose I have this integration test
class TestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.key = '123'
def test_01_create_acc(self):
user = create_account(...)
self.key = user.key
def test_02_check_account(self):
user = check_account(..)
self.assertEqual(self.key, user.key)
It looks like the attribute self.key is not mutable. It stays with the old value from setUpClass. But isn't setUpClass only called once?
The account function creates a key randomly for security reason, so I am not allowed to pass in my secret key. It returns the key, so I need to modify that attribute. Can I?
It looks like each test_ case is isolated.
my_gloabl = None
def setUpClass(cls):
cls.key = my_global
If I change my_global in test1, test2 will get None.

The class is set up only once. But each test method is actually called from a different instance of that test.
You can demonstrate this using the id function, which will return a different number for each object:
import unittest
class TestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
print "setup"
def test_01_create_acc(self):
print id(self)
def test_02_check_account(self):
print id(self)
unittest.main()
On my computer, this printed:
setup
4300479824
.4300479888
Note how the setup method was called only once, but the id of the instance for test1 and test2 are different.

Related

Python unittesting - How to assert inside of setUpClass?

In my setUpClass I would like to create a resource in the database one time, which is then used for all of the tests in the class.
After I create the resource in setUpClass, I would like to perform assertions on it right then and there. However, I'm not sure how to call assertions in setUpClass, given that all of the assertion functions are instance methods, not class methods.
import unittest
class TestFoo(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
# How would I assert that cls.foo['name'] == 'bar'?
# The following does not work, as the assertEquals function is not a class method
# cls.assertEquals(cls.foo['name'], 'bar')
#classmethod
def make_foo(cls, name):
# Calls a POST API to create a resource in the database
return {'name': name}
def test_foo_0(self):
# Do something with cls.foo
pass
def test_foo_1(self):
# do something else with cls.foo
pass
The only alternative I can think of is to raise an exception in setUpClass:
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
if cls.foo['name'] != 'bar':
raise Exception("Failed to create resource, cannot do the tests")
Of course, I do not want to call the assertions from each test, as this will just duplicate the code.
Edit: I don't think this workaround is good, because the failure message will point to the self.assertFalse(self.flag) line, instead of the if cls.foo['name'] ~= 'bar' line. In addition, if you created multiple resources, this would need multiple flags to disambiguate.
flag=False
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
if cls.foo['name'] != 'bar':
cls.flag=True
def setUp(self):
self.assertFalse(self.flag)

Python unittest patch mock entire class

I have a class that I want to patch in my unittests.
class OriginalClass():
def method_a():
# do something
def method_b():
# do another thing
Now I created another class to patch it with, so the code for patching it is like
class MockClass(OriginalClass):
def method_a():
# This will override the original method and return custom response for testing.
patcher = patch('OriginalClass', new=MockClass)
mock_instance = patcher.start()
This works exactly as I want it to and I can return whatever responses required for my unittests.
Now this issue is when I want to verify that a method is called with the right parameters in the unittests.
I tried
mock_instance.method_a.assert_called_once()
But it fail with error AttributeError: 'function' object has no attribute 'assert_called_once'.
How can I test the method calls here?
AttributeError: 'function' object has no attribute 'assert_called_once'.
Once mock object is created, there is no method_a exists, you have to call once m.method_a() before assert.
m = mock.create_autospec(OriginalClass)
m.method_a()
m.method_a.assert_called_once()
patch mock entire class
I took it as mock the whole class and all its methods, I would take an example from here
https://docs.python.org/3.3/library/unittest.mock-examples.html
Applying the same patch to every test method, Here is my example, patch the entire Primary class as MockPrimay for every methods and every tests, setup or SetupClass could be added for the methods needed, even the whole class is mocked, but not every methods to be used in the tests.
from tests.lib.primary_secondary import Secondary
#mock.patch('tests.lib.primary_secondary.Primary')
class TestSecondaryMockPrimary(unittest.TestCase):
def test_method_d(self, MockPrimary):
MockPrimary().process()
MockPrimary().process.return_value = 1
oc = Secondary()
self.assertEqual(oc.method_d(), 1)
import tests
self.assertIs(tests.lib.primary_secondary.Primary, MockPrimary)
The Primary is needed for the Secondary for this test
class Primary(object):
def __init__(self, param):
self._param = param
def process(self):
if self._param == 1:
self._do_intermediate_process()
self._do_process()
class Secondary(object):
def __init__(self):
self.scl = Primary(1)
def method_d(self):
return self.scl.process
I think wraps can be useful here:
from unittest.mock import patch
class Person:
name = "Bob"
def age(self):
return 35
class Double(Person):
def age(self):
return 5
with patch('__main__.Person', wraps=Double()) as mock:
print(mock.name) # mocks data
print(mock.age()) # runs real methods, but still spies their calls
mock.age.assert_not_called()
Output:
<MagicMock name='Person.name' id='139815250247536'>
5
...
raise AssertionError(msg)
AssertionError: Expected 'age' to not have been called. Called 1 times.
Calls: [call()].

How to access the variables created within a `with` statement

I have defined a python context class and a Test class in a file:
class Test(object):
pass
class MyContext(object):
def __init(self):
self._vars = []
def __enter__(self):
pass
def __exit(self, ....):
pass
In another file using that context:
from somewhere import Test, MyContext
with MyContext() as ctx:
mytest = Test()
So what I want to achieve is that when I exit the context, I want to be aware of the mytest instance created and add it in the ctx._vars = [<instance of Test >].
I don't want to have a ctx.add_var(mytest) method, I want those Test instances to be added automatically to the ctx instance.
That is possible of being done, using Python's introspection capabilities, but you have to be aware this is not what the with context block was created for.
I agree it is a useful syntax construction that can be "deviated" to do things like what you want: annotate the objects created inside a code block in a "registry".
Before showing how to do that with a context manager consider if a class body would not suffice you. Using a class body this way also deviates from its primary purpose, but you have your "registry" for free:
from somewhere import Test, MyContext
class ctx:
mytest = Test()
vars = ctx.__dict__.values()
In order to do that with a context manager, you have to inspect the local variables at the start and at the end of the with block. While that is not hard to do, it wuld not cover all instances of Test created - because if the code is like this:
mytests = []
with Mycontext as ctx:
mytests.append(Test())
No new variable is created - so code tracking the local variables would not find anything. Code could be written to look recursively into variables with containers, such as dictionaries and lists - but then mytest() instances could be added to a container referenced as a global variable, or a variable in other module.
It turns out that a reliable way to track Test instances would be to instrument the Test class itself to annotate new instances ina registry. That is far easier and less depentend on "local variable introspection" tricks.
The code for that is somewhat like:
class Test(object):
pass
class MyContext(object):
def __init(self, *args):
self.vars = []
self.track = args
self.original_new = {}
def patch(self, cls_to_patch):
cls_new = getattr(cls_to_patch, "__new__")
if "__new__" in cls.__dict__:
self.original_new[cls_to_patch] = cls_new
def patched_new(cls, *args, **kwargs):
instance = cls_new(*args, **kwags)
self.vars.append(instance)
return instance
cls_to_patch.__new__ = patched_new
def restore(self, cls):
if cls in self.original_new:
# class had a very own __new_ prior to patching
cls.__new__ = self.original_new[cls]
else:
# just remove the wrapped new method, restores access to superclass `__new__`
del cls.__new__
def __enter__(self):
for cls in self.track:
self.patch(cls)
return self
def __exit(self, ....):
for cls in self.track:
self.restore(cls)
...
from somewhere import Test, MyContext
with MyContext(Test) as ctx:
mytest = Test()

How can I access a value which is defined in a __init__ function, as a parameter to my test_case

I have my test case data, which is created in init function. I need to use it as a parameter for my test case, but unable to access test_case_data
class something(object):
def __init__(self, logger):
#do something
self.test_case_data = ... #Got test case data
#pytest.mark.p0
#pytest.parameterize("paramter1, parameter2, .. , ..", test_case_data)
def test_something():
#do_something
This is what my code looks like. I want to use test_case_data as a parameter, which is defined in a init function.
init isn't run until you make an instance of the class. To access:
class ClassName(object):
def __init__(self):
self.variable = "value"
instance = ClassName()
print(instance.variable)
I'm not sure what you're trying to do here but the thing to remember is that those data members declared in init don't exist until you make an instance of the class.

Python Unittest, Inheritance, and Member Variable Propagation

I'm using Python 2.6. Let's say I have 2 classes:
class BaseTest(unittest.TestCase):
def test_a(self):
print 'test_a'
self.random_variable = random_string()
print self.random_variable
class SubTest1(BaseTest):
def test_b(self):
print 'test_b'
print self.random_variable
Where random_string() returns a randomly generated 20 character string.
I'd like to be able to access self.random_variable in SubTest1.test_b. Right now, self.random_variable in test_b is undefined.
When BaseTest runs, it would have its own unique random string, but when SubTest1 runs, it would have the same string generated in BaseTest. The output for all of this would ideally look something like this:
test_a
dgkwgmhkiszvmlhceved
test_a
akvjkskdmhfygsysgjci
test_b
akvjkskdmhfygsysgjci
Is this possible?
One of the principles of unit testing is that every test should be isolated from, and independent of, every other test. If you have test work that you want done in more than one test, then put it in a helper function, and call that function from the tests:
class MyTests(unittest.TestCase):
def do_common_stuff(self):
self.random_variable = random_string()
#... do more stuff you want in more than one test ...
def test_a(self):
self.do_common_stuff()
def test_b(self):
self.do_common_stuff()
#... do more stuff ...
Yes, you can define the variable in the setUp method which runs before each test case (so that's then different for each test):
class BaseTest(unittest.TestCase):
def setUp(self):
self.random_variable = random_string()
def test_a(self):
print 'test_a'
print self.random_variable
If you want it to be the same you'll need to use setUpClass:
class BaseTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
self.random_variable = random_string()
def test_a(self):
print 'test_a'
print self.random_variable
Note that marking setUpClass with #classmethod is important as it turns the method from a regular method into a class method (which receives the class rather than the instance as first argument).

Categories

Resources