Add doc for running unit tests
Change-Id: Ib6a55b65767e89eb5411783934aa3cc362505dcb
This commit is contained in:
parent
af1378a8e7
commit
ce8014eca9
@ -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
|
||||
----------
|
||||
|
Loading…
x
Reference in New Issue
Block a user