I am working on a Django project and some unittests seemed to be failing seemingly unexpectedly. I was able to track down the failures to how it seems the VSCode/unittest testing framework is calling the test cases. I was able to reproduce the behavior with the simple setup below.
import unittest
class USER_STORY_1_8_1(unittest.TestCase):
def setUp(self):
print('USER_STORY_1_8_1:setUp')
def tearDown(self):
print('USER_STORY_1_8_1:tearDown')
def test_run(self):
print('USER_STORY_1_8_1:test_run')
class USER_STORY_1_8_10(unittest.TestCase):
def setUp(self):
print('USER_STORY_1_8_10:setUp')
def tearDown(self):
print('USER_STORY_1_8_10:tearDown')
def test_run(self):
print('USER_STORY_1_8_10:test_run')
class USER_STORY_1_8_11(unittest.TestCase):
def setUp(self):
print('USER_STORY_1_8_11:setUp')
def tearDown(self):
print('USER_STORY_1_8_11:tearDown')
def test_run(self):
print('USER_STORY_1_8_11:test_run')
Screen Capture of VSCode project Here
It seems that for similarly named test cases like these:
class USER_STORY_1_8_1(unittest.TestCase)
class USER_STORY_1_8_10(unittest.TestCase)
class USER_STORY_1_8_11(unittest.TestCase)
instead of calling each one in an exact-match type of way, it's doing some kind of 'USER_STORY_1_8_1.*' ordered resolution so that a call to USER_STORY_1_8_1 actually calls into the setUp() for USER_STORY_1_8_11. If USER_STORY_1_8_11 is unavailable (e.g. commented out), then i resolves to USER_STORY_1_8_10. And if neither is available, only then it calls the correct USER_STORY_1_8_1.
Is there anyway to override this behavior?
Related
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")
I am trying to share class scope and method scope fixtures across different classes using pytest. Currently, I get a syntax error if I try to add a fixture from another class as shown below.
I am aware that one way to work around this is to not have the fixture enclosed by a class. However, enclosing the fixtures makes sense for what I am trying to do.
import pytest
class Test_example1(object):
#classmethod
#pytest.fixture(scope='class')
def example1_fixture(self):
print("example1_fixture setup")
yield
print("example1_fixture teardown")
def test_1(self, example1_fixture):
print("class example1::test_1")
# This works if I comment out the code causing errors
#pytest.fixture(scope='class')
def global_example_fixture():
print("global_example_fixture setup")
yield
print("global_example_fixture teardown")
class Test_example2(object):
#pytest.fixture(scope='class')
def example2_fixture(self):
print("example2_fixture setup")
yield
print("example2_fixture teardown")
# Results in fixture not found error
def test_1(self, example1_fixture):
print("class example2::test_1")
# Results in syntax error
def test_2(self, Test_example1.example1_fixture, global_example_fixture):
print("class example2::test_1")
# This works...
def test_2(self, example2_fixture, global_example_fixture):
print("class example2::test_2")
I expect to be able to call the class level and method level fixtures across classes.
I need to organize my test cases because I have a large test suite. I can't see to get a test in one Python class to be skipped if a test it depends on in another Python class fails.
Here is my basic setup:
class TestWorkflow1:
#staticmethod
#pytest.mark.dependency()
def test_create_something():
//do some stuff
class TestNegativeWorkflowClone1:
#staticmethod
#pytest.mark.dependency('TestWorkflow1::test_create_something')
def test_try_to_clone_something():
//do some stuff
TestNegativeWorkflowClone1 runs before TestWorkflow1. I have tried what was suggested in an answer to this ticket: Dependencies between files with pytest-dependency?
from pytest_dependency import DependencyManager
class TestWorkflow1:
DependencyManager.ScopeCls['module'] = DependencyManager.ScopeCls['session']
#staticmethod
#pytest.mark.dependency()
def test_create_something():
//do some stuff
from pytest_dependency import DependencyManager
class TestNegativeWorkflowClone1:
DependencyManager.ScopeCls['module'] = DependencyManager.ScopeCls['session']
#staticmethod
#pytest.mark.dependency('TestWorkflow1::test_create_something')
def test_try_to_clone_something():
//do some stuff
That didn't work either. TestNegativeWorkflowClone1 still runs before TestWorkflow1.
I tried using the filename in the dependency decoration in TestNegativeWorkflowClone1
class TestNegativeWorkflowClone1:
DependencyManager.ScopeCls['module'] = DependencyManager.ScopeCls['session']
#staticmethod
#pytest.mark.dependency('TestWorkflow1.py::test_create_something')
def test_try_to_clone_something():
//do some stuff
Still didn't work. TestNegativeWorkflowClone1 still runs first.
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.
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.