Merge "Clarify unittest documentation"

This commit is contained in:
Jenkins 2016-07-20 11:11:03 +00:00 committed by Gerrit Code Review
commit d41d773cb3
2 changed files with 61 additions and 22 deletions

View File

@ -39,45 +39,53 @@ on the runtime environment, making them difficult to debug.
There are several possible strategies available for dealing with dangling There are several possible strategies available for dealing with dangling
mock objects (see the section on recommended patterns). mock objects (see the section on recommended patterns).
Further information is available in [1]_. Further information is available in [1]_, [2]_, [3]_.
Dangling Mock Detector Dangling Mock Detector
---------------------- ----------------------
All Trove unit tests should extend 'trove_testtools.TestCase'. All Trove unit tests should extend 'trove_testtools.TestCase'.
It is a subclass of 'testtools.TestCase' which automatically checks for It is a subclass of 'testtools.TestCase' which automatically checks for
dangling mock objects after each test. dangling mock objects at each test class teardown.
It does that by recording mock instances in loaded modules before and after It marks the tests as failed and reports the leaked reference if it
a test case. It marks the test as failed and reports the leaked reference if it
finds any. finds any.
Recommended Mocking Patterns Recommended Mocking Patterns
---------------------------- ----------------------------
- Mocking a class or object shared across multiple test cases. Mocking a class or object shared across multiple test cases.
Use the patcher pattern in conjunction with the setUp() and tearDown() Use the patcher pattern in conjunction with the setUp()
methods [ see section 26.4.3.5. of [1]_ ]. method [ see section 26.4.3.5. of [1]_ ].
.. code-block:: python .. code-block:: python
def setUp(self): def setUp(self):
super(CouchbaseBackupTests, self).setUp() super(CouchbaseBackupTests, self).setUp()
self.exe_timeout_patch = patch.object(utils, 'execute_with_timeout') self.exe_timeout_patch = patch.object(utils, 'execute_with_timeout')
self.addCleanup(self.exe_timeout_patch.stop)
def test_case(self): def test_case(self):
# This line can be moved to the setUp() method if the mock object mock_exe_timeout = self.exe_timeout_patch.start()
# is not needed.
mock_object = self.exe_timeout_patch.start()
def tearDown(self): If the mock object is required in the majority of test cases the following
super(CouchbaseBackupTests, self).tearDown() pattern may be more efficient.
self.exe_timeout_patch.stop()
Note also: patch.stopall() .. code-block:: python
This method stops all active patches that were started with start.
- Mocking a class or object for a single entire test case. def setUp(self):
Use the decorator pattern. super(CouchbaseBackupTests, self).setUp()
self.exe_timeout_patch = patch.object(utils, 'execute_with_timeout')
self.addCleanup(self.exe_timeout_patch.stop)
self.mock_exe_timeout = self.exe_timeout_patch.start()
def test_case(self):
# All test cases can now reference 'self.mock_exe_timeout'.
- Note also: patch.stopall()
This method stops all active patches that were started with start.
Mocking a class or object for a single entire test case.
Use the decorator pattern.
.. code-block:: python .. code-block:: python
@ -91,8 +99,8 @@ This method stops all active patches that were started with start.
def test_case(self, generate_random_password, execute_with_timeout): def test_case(self, generate_random_password, execute_with_timeout):
pass pass
- Mocking a class or object for a smaller scope within one test case. Mocking a class or object for a smaller scope within one test case.
Use the context manager pattern. Use the context manager pattern.
.. code-block:: python .. code-block:: python
@ -109,7 +117,35 @@ This method stops all active patches that were started with start.
password_mock = mocks['generate_random_password'] password_mock = mocks['generate_random_password']
execute_mock = mocks['execute_with_timeout_mock'] execute_mock = mocks['execute_with_timeout_mock']
Mocking global configuration properties.
Use 'patch_conf_property' method from 'trove_testtools.TestCase'.
.. code-block:: python
def test_case(self):
self.patch_conf_property('max_accepted_volume_size', 10)
Datastore-specific configuration properties can be mocked by passing
an optional 'section' argument to the above call.
.. code-block:: python
def test_case(self):
self.patch_conf_property('cluster_support', False, section='redis')
- Note also: 'patch_datastore_manager()'
'datastore_manager' name has to be set properly when testing
datastore-specific code to ensure correct configuration options get loaded.
This is a convenience method for mocking 'datastore_manager' name.
.. code-block:: python
def test_case(self):
self.patch_datastore_manager('cassandra')
References References
---------- ----------
.. [1] Mock Guide: https://docs.python.org/3/library/unittest.mock.html .. [1] Mock Guide: https://docs.python.org/3/library/unittest.mock.html
.. [2] Python Mock Gotchas: http://alexmarandon.com/articles/python_mock_gotchas/
.. [3] Mocking Mistakes: http://engineroom.trackmaven.com/blog/mocking-mistakes/

View File

@ -166,9 +166,12 @@ class TestCase(testtools.TestCase):
def patch_datastore_manager(self, manager_name): def patch_datastore_manager(self, manager_name):
return self.patch_conf_property('datastore_manager', manager_name) return self.patch_conf_property('datastore_manager', manager_name)
def patch_conf_property(self, property_name, value): def patch_conf_property(self, property_name, value, section=None):
target = cfg.CONF
if section:
target = target.get(section)
conf_patcher = mock.patch.object( conf_patcher = mock.patch.object(
cfg.CONF, property_name, target, property_name,
new_callable=mock.PropertyMock(return_value=value)) new_callable=mock.PropertyMock(return_value=value))
self.addCleanup(conf_patcher.stop) self.addCleanup(conf_patcher.stop)
return conf_patcher.start() return conf_patcher.start()