How to mock up a class for several tests in Django - python

I have a class that calls a remote service through HTTP. Right now, this class detects if it is running in "TESTING" mode and acts accordingly: while "TESTING" it does not send the actual request to the remote service, it simply returns without executing anything at all.
class PushService(object):
def trigger_event(self, channel_name, event_name, data):
if satnet_cfg.TESTING:
logger.warning('[push] Service is in testing mode')
return
self._service.trigger(channel_name, event_name, data)
Several tests invoke parts of the code that end up by invoking this method. My questions are the following:
1. Do I have to patch this method/class for every test that, for some reason, also invoke that method?
2. Is it a good practice to try to patch it in the TestRunner?

If you need to do patch for all the tests you can do this in setUpClass method:
class RemoteServiceTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.patchers = []
patcher = patch('application.PushService.trigger_event')
cls.patchers.append(patcher)
trigger_mock = patcher.start()
trigger_mock.return_value = 'Some return value'
#classmethod
def tearDownClass(cls):
for patcher in cls.patchers:
patcher.stop()
def test1(self):
# Test actions
def test2(self):
# Test actions
def test3(self):
# Test actions
setUpClass is called once per class (test suite in this case). Inside this method you can set all patchers that you need to be used by all tests.

Related

Mock an external service instantiated in python constructor

I'm working on unit tests for a service I made that uses confluent-kafka. The goal is to test successful function calls, exception errors, etc. The problem I'm running into is since I'm instantiating the client in the constructor of my service the tests are failing since I'm unsure how to patch a constructor. My question is how do I mock my service in order to properly test its functionality.
Example_Service.py:
from confluent_kafka.schema_registry import SchemaRegistryClient
class ExampleService:
def __init__(self, config):
self.service = SchemaRegistryClient(config)
def get_schema(self):
return self.service.get_schema()
Example_Service_tests.py
from unittest import mock
#mock.patch.object(SchemaRegistryClient, "get_schema")
def test_get_schema_success(mock_client):
schema_Id = ExampleService.get_schema()
mock_service.assert_called()
The problem is that you aren't creating an instance of ExampleService; __init__ never gets called.
You can avoid patching anything by allowing your class to accept a client maker as an argument (which can default to SchemaRegistryClient:
class ExampleService:
def __init__(self, config, *, client_factory=SchemaRegistryClient):
self.service = client_factory(config)
...
Then in your test, you can simply pass an appropriate stub as an argument:
def test_get_schema_success():
mock_client = Mock()
schema_Id = ExampleService(some_config, client_factory=mock_client)
mock_client.assert_called()
Two ways
mock entire class using #mock.patch(SchemaRegistryClient) OR
replace #mock.patch.object(SchemaRegistryClient, "get_schema") with
#mock.patch.object(SchemaRegistryClient, "__init__")
#mock.patch.object(SchemaRegistryClient, "get_schema")

Is there a way to run "setup" only once for the entire suite of Python tests? [duplicate]

Is there a function that is fired at the beginning/end of a scenario of tests? The functions setUp and tearDown are fired before/after every single test.
I typically would like to have this:
class TestSequenceFunctions(unittest.TestCase):
def setUpScenario(self):
start() #launched at the beginning, once
def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq)
def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element in self.seq)
def tearDownScenario(self):
end() #launched at the end, once
For now, these setUp and tearDown are unit tests and spread in all my scenarios (containing many tests), one is the first test, the other is the last test.
As of 2.7 (per the documentation) you get setUpClass and tearDownClass which execute before and after the tests in a given class are run, respectively. Alternatively, if you have a group of them in one file, you can use setUpModule and tearDownModule (documentation).
Otherwise your best bet is probably going to be to create your own derived TestSuite and override run(). All other calls would be handled by the parent, and run would call your setup and teardown code around a call up to the parent's run method.
I have the same scenario, for me setUpClass and tearDownClass methods works perfectly
import unittest
class Test(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
#classmethod
def tearDownClass(cls):
cls._connection.destroy()
Here is an example: 3 test methods access a shared resource, which is created once, not per test.
import unittest
import random
class TestSimulateLogistics(unittest.TestCase):
shared_resource = None
#classmethod
def setUpClass(cls):
cls.shared_resource = random.randint(1, 100)
#classmethod
def tearDownClass(cls):
cls.shared_resource = None
def test_1(self):
print('test 1:', self.shared_resource)
def test_2(self):
print('test 2:', self.shared_resource)
def test_3(self):
print('test 3:', self.shared_resource)
For python 2.5, and when working with pydev, it's a bit hard. It appears that pydev doesn't use the test suite, but finds all individual test cases and runs them all separately.
My solution for this was using a class variable like this:
class TestCase(unittest.TestCase):
runCount = 0
def setUpClass(self):
pass # overridden in actual testcases
def run(self, result=None):
if type(self).runCount == 0:
self.setUpClass()
super(TestCase, self).run(result)
type(self).runCount += 1
With this trick, when you inherit from this TestCase (instead of from the original unittest.TestCase), you'll also inherit the runCount of 0. Then in the run method, the runCount of the child testcase is checked and incremented. This leaves the runCount variable for this class at 0.
This means the setUpClass will only be ran once per class and not once per instance.
I don't have a tearDownClass method yet, but I guess something could be made with using that counter.
import unittest
class Test(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.shared_data = "dddd"
#classmethod
def tearDownClass(cls):
cls.shared_data.destroy()
def test_one(self):
print("Test one")
def test_two(self):
print("Test 2")
For more visit Python
unit test document

Python unit tests run function after all test

I need to test smth on python via ssh. I don't want to make ssh connection for every test, because it is to long, I have written this:
class TestCase(unittest.TestCase):
client = None
def setUp(self):
if not hasattr(self.__class__, 'client') or self.__class__.client is None:
self.__class__.client = paramiko.SSHClient()
self.__class__.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.__class__.client.connect(hostname=consts.get_host(), port=consts.get_port(), username=consts.get_user(),
password=consts.get_password())
def test_a(self):
pass
def test_b(self):
pass
def test_c(self):
pass
def disconnect(self):
self.__class__.client.close()
and my runner
if __name__ == '__main__':
suite = unittest.TestSuite((
unittest.makeSuite(TestCase),
))
result = unittest.TextTestRunner().run(suite)
TestCase.disconnect()
sys.exit(not result.wasSuccessful())
In this version I get error TypeError: unbound method disconnect() must be called with TestCase instance as first argument (got nothing instead). So how i can call disconnect after all tests pass?
With best regards.
You should use setUpClass and tearDownClass instead, if you want to keep the same connection for all tests. You'll also need to make the disconnect method static, so it belongs to the class and not an instance of the class.
class TestCase(unittest.TestCase):
def setUpClass(cls):
cls.connection = <your connection setup>
#staticmethod
def disconnect():
... disconnect TestCase.connection
def tearDownClass(cls):
cls.disconnect()
you can do it by defining startTestRun,stopTestRun of unittest.TestResult class. setUpClass and tearDownClass are running per test class(per test file) so if you have multiple files this methods will run for each one.
by adding following code to my tests/__init__.py i managed to achieve it. this code runs only once for all tests(regardless of number of test classes and test files).
def startTestRun(self):
"""
https://docs.python.org/3/library/unittest.html#unittest.TestResult.startTestRun
Called once before any tests are executed.
:return:
"""
DockerCompose().start()
setattr(unittest.TestResult, 'startTestRun', startTestRun)
def stopTestRun(self):
"""
https://docs.python.org/3/library/unittest.html#unittest.TestResult.stopTestRun
Called once after all tests are executed.
:return:
"""
DockerCompose().compose.stop()
setattr(unittest.TestResult, 'stopTestRun', stopTestRun)
There seems to be a simple solution for the basic (beginner's) case:
def tmain():
setup()
unittest.main(verbosity=1, exit=False)
clean()
The trick is "exit=False" which lets the function tmain run until the end.
setup() and clean() can be any functions to do what the name implies.

Append the nose #attr to the test name

I am able to setup nose tests to run with the #attr tag. I am now interested in know if I can append to the end of the test name, the #attr tag? What we are trying to do is add a tag if our tests run into an issue and we write up a defect for it, we would then put the defect number as an #attr tag. Then when we run we could easily identify which tests have open defects against them.
Just wondering if this is even possible, and where to go to see how to set it up?
EDIT RESULTS RUNNING WITH ANSWER:
Test Results:
So I sort of know what is going on, if I have the #fancyattr() at the class level it picks it up and changes the name of the class. When I put the #fancyattr() at the test level it is not changing the name of the test, which is what I need for it to do.
For example - Changes the name of the class:
#dms_attr('DMSTEST')
#attr('smoke_login', 'smoketest', priority=1)
class TestLogins(BaseSmoke):
"""
Just logs into the system and then logs off
"""
def setUp(self):
BaseSmoke.setUp(self)
def test_login(self):
print u"I can login -- taking a nap now"
sleep(5)
print u"Getting off now"
def tearDown(self):
BaseSmoke.tearDown(self)
This is what I need and it isn't working:
#attr('smoke_login', 'smoketest', priority=1)
class TestLogins(BaseSmoke):
"""
Just logs into the system and then logs off
"""
def setUp(self):
BaseSmoke.setUp(self)
#dms_attr('DMSTEST')
def test_login(self):
print u"I can login -- taking a nap now"
sleep(5)
print u"Getting off now"
def tearDown(self):
BaseSmoke.tearDown(self)
Updated screenshot with what I am seeing with __doc__:
Here is how to do it with args type attributes:
rename_test.py:
import unittest
from nose.tools import set_trace
def fancy_attr(*args, **kwargs):
"""Decorator that adds attributes to classes or functions
for use with the Attribute (-a) plugin. It also renames functions!
"""
def wrap_ob(ob):
for name in args:
setattr(ob, name, True)
#using __doc__ instead of __name__ works for class methods tests
ob.__doc__ = '_'.join([ob.__name__, name])
#ob.__name__ = '_'.join([ob.__name__, name])
return ob
return wrap_ob
class TestLogins(unittest.TestCase):
#fancy_attr('slow')
def test_method():
assert True
#fancy_attr('slow')
def test_func():
assert True
Running test:
$ nosetests rename_test.py -v
test_method_slow ... ok
test_func_slow ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
EDIT: For xunit reporting to work, test renaming should take place before running the test. You can do it on import, here is untested hack showing how to do it:
rename_test.py:
import unittest
def fancy_attr(*args, **kwargs):
"""Decorator that adds attributes to classes or functions
for use with the Attribute (-a) plugin. It also renames functions!
"""
def wrap_ob(ob):
for name in args:
setattr(ob, name, True)
ob.__doc__ = '_'.join([ob.__name__, name])
return ob
return wrap_ob
class TestLogins(unittest.TestCase):
#fancy_attr('slow')
def test_method(self):
assert True
def make_name(orig, attrib):
return '_'.join([orig, attrib])
def rename(cls):
methods = []
for key in cls.__dict__:
method = getattr(cls, key)
if method:
if hasattr(cls.__dict__[key], '__dict__'):
if 'slow' in cls.__dict__[key].__dict__:
methods.append(key)
print methods
for method in methods:
setattr(cls, make_name(method, 'slow'), cls.__dict__[key])
delattr(cls, method)
rename(TestLogins)
#fancy_attr('slow')
def test_func():
assert True

Avoid passing second param to every unit test using mock.patch

I'm mocking my RpcClient class for all of my unit tests like this:
import unittest2
from mock import patch
#patch('listeners.RpcClient')
class SomeTestCase(unittest2.TestCase):
test_something(self, mock_api):
...
test_something_else(self, mock_api):
...
For most of my tests I don't want to do any assertions using the mock object, all I want to do is patch the class so the RpcClient doesn't attempt to connect and fire requests for each of my tests (I have it hooked up to a post save event on one of my models).
Can I avoid passing in mock_api into every single one of my tests?
I ended up doing the mocking in setUp using patcher.start():
def setUp(self):
self.rpc_patcher = patch('listeners.RpcClient')
self.MockClass = rpc_patcher.start()
def tearDown(self):
self.rpc_patcher.stop()
So I don't have to decorate any of my test cases and don't have to add any extra arguments to my tests.
More info:
http://docs.python.org/dev/library/unittest.mock#patch-methods-start-and-stop
Can you just set a default parameter to mock_api?
def test_something(self, mock_api=None):
...
def test_something_else(self, mock_api=None):
You could make use mock.patch as a context manager for when you call the SUT. Something like:
import unittest2
from mock import patch
class SomeTestCase(unittest2.TestCase):
def call_sut(self, *args, **kwargs):
with mock.patch('listeners.RpcClient'):
# call SUT
def test_something(self):
self.call_sut()
# make assertions
def test_something_else(self):
self.call_sut()
# make assertions

Categories