Add doc for running unit tests

Change-Id: Ib6a55b65767e89eb5411783934aa3cc362505dcb
This commit is contained in:
Lingxian Kong 2021-07-13 09:23:46 +12:00
parent af1378a8e7
commit ce8014eca9
1 changed files with 53 additions and 81 deletions

View File

@ -1,8 +1,8 @@
.. _testing:
=========================
Notes on Trove Unit Tests
=========================
================
Trove Unit Tests
================
Mock Object Library
-------------------
@ -12,7 +12,7 @@ This library lets the caller replace (*"mock"*) parts of the system under test w
mock objects and make assertions about how they have been used. [1]_
The Problem of Dangling Mocks
-----------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Often one needs to mock global functions in shared system modules.
The caller must restore the original state of the module
@ -42,7 +42,7 @@ mock objects (see the section on recommended patterns).
Further information is available in [1]_, [2]_, [3]_.
Dangling Mock Detector
----------------------
~~~~~~~~~~~~~~~~~~~~~~
All Trove unit tests should extend 'trove_testtools.TestCase'.
It is a subclass of 'testtools.TestCase' which automatically checks for
@ -50,98 +50,70 @@ dangling mock objects at each test class teardown.
It marks the tests as failed and reports the leaked reference if it
finds any.
Recommended Mocking Patterns
----------------------------
Writing Unit Tests
------------------
Trove has some legacy unit test code for all the components which is not
recommended to follow. Use the suggested approaches below.
Mocking a class or object shared across multiple test cases.
Use the patcher pattern in conjunction with the setUp()
method [ see section 26.4.3.5. of [1]_ ].
Writing Unit Tests for Trove API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For trove-api unit test, we use real database (sqlite).
Set up trove database in ``setUpClass`` method.
.. code-block:: python
from trove.tests.unittests.util import util
@classmethod
def setUpClass(cls):
util.init_db()
and clean up the database in the method ``tearDownClass``:
.. code-block:: python
from trove.tests.unittests.util import util
@classmethod
def tearDownClass(cls):
util.cleanup_db()
Insert some data in ``setUpClass`` in order to run the tests.
Trove sends notifications for various operations which communicates with
the message queue service. In unit test, this is also mocked and usually
called in the ``setUp`` method.
.. code-block:: python
from trove.tests.unittests import trove_testtools
def setUp(self):
super(CouchbaseBackupTests, self).setUp()
self.exe_timeout_patch = patch.object(utils, 'execute_with_timeout')
self.addCleanup(self.exe_timeout_patch.stop)
trove_testtools.patch_notifier(self)
def test_case(self):
mock_exe_timeout = self.exe_timeout_patch.start()
Look at an example in ``trove/tests/unittests/instance/test_service.py``
If the mock object is required in the majority of test cases the following
pattern may be more efficient.
Run Unit Test
-------------
.. code-block:: python
Run all the unit tests in one command:
def setUp(self):
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()
.. code-block:: console
def test_case(self):
# All test cases can now reference 'self.mock_exe_timeout'.
tox -e py38
- Note also: patch.stopall()
This method stops all active patches that were started with start.
Run all the tests of a specific test class:
Mocking a class or object for a single entire test case.
Use the decorator pattern.
.. code-block:: console
.. code-block:: python
tox -e py38 -- trove.tests.unittests.instance.test_service.TestInstanceController
@patch.object(utils, 'execute_with_timeout')
@patch.object(os, 'popen')
def test_case(self, popen_mock, execute_with_timeout_mock):
pass
Run a single test case:
@patch.multiple(utils, execute_with_timeout=DEFAULT,
generate_random_password=MagicMock(return_value=1))
def test_case(self, generate_random_password, execute_with_timeout):
pass
.. code-block:: console
Mocking a class or object for a smaller scope within one test case.
Use the context manager pattern.
.. code-block:: python
def test_case(self):
# Some code using real implementation of 'generate_random_password'.
with patch.object(utils, 'generate_random_password') as pwd_mock:
# Using the mocked implementation of 'generate_random_password'.
# Again code using the actual implementation of the method.
def test_case(self):
with patch.multiple(utils, execute_with_timeout_mock=DEFAULT,
generate_random_password=MagicMock(
return_value=1)) as mocks:
password_mock = mocks['generate_random_password']
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')
tox -e py38 -- trove.tests.unittests.instance.test_service.TestInstanceController.test_create_multiple_versions
References
----------