Clarify unittest documentation
- Update the description. Dangling mocks are no longer detected after every single test case. - Use a preffered pattern for unmocking. - Allow the 'patch_conf_property' take an optional 'section' argument and add examples on configuration mocking to the doc. - Add extra examples on object mocking. Change-Id: I9dc6d26ca879b0d5e1b16d1bdbc97b2a81351781
This commit is contained in:
parent
d58baf220a
commit
e8783520e1
@ -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/
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user