Providing unit tests

So far, we provided an overview of the design and development process surrounding microservices, based for the most part on their ideation and implementation phases.

However, after providing the foundation for the design on features using the Services stack available to Omniverse applications, we would be remiss if we did not mention best practices around testing. This is especially important in the context where consumers of features may wish to validate that healthy development practices were followed before building on top of features made available to them.

About unit tests in Omniverse applications

Omniverse Extensions feature out-of-the-box support for unit testing Python components, building on the rich set of features provided by the unittest module.

For brevity, as the focus of this tutorial series is to provide you with the best practices surrounding healthy development habits for Services, we will limit the number of unit tests shared here to only a few.

Know, however, that while development teams agree that a high percentage of code coverage generally tend to indicate robust and healthy development practices, the most important aspect of testing and automation is generally having teams openly discuss and regularly revisit their goals and objectives. As with design and development practices, clear communication and the participation of all team members play a major role in the achievement of a high-quality codebase.

Note

For a detailed tour of the unit testing features offered to Omniverse Extensions, consult the Kit SDK documentation.

Unit tests for our Service’s utils module

Let’s include a few unit tests for the utils module of the Service we presented earlier:

exts/omni.services.example.viewport_capture.core/omni/services/example/viewport_capture/core/tests/test_utils.py
 1import carb.settings
 2
 3import omni.kit.test
 4
 5# Let's import the components for which we intend to provide unit tests from their module paths:
 6from omni.services.example.viewport_capture.core.utils import (
 7    get_captured_image_directory,
 8    get_captured_image_path,
 9    get_extension_name,
10)
11
12
13# Unit tests for Omniverse Extensions follow the standard patterns and best practices of the Python community, and
14# already well-defined around the `unittest` module.
15#
16# In case this is your first experience writing unit tests, just know that having a test class derived from
17# `omni.kit.test.AsyncTestCase` declared on the root of module will make it auto-discoverable by the test framework,
18# and the methods prefixed with the `test_*` keyword will be automatically executed.
19class TestUtils(omni.kit.test.AsyncTestCase):
20    """Unit tests for the `utils` module."""
21
22    def _get_setting(self, setting_key: str) -> str:
23        """
24        Utility method to return extension settings.
25
26        Args:
27            setting_key (str): Key of the setting to retrieve.
28
29        Returns:
30            str: The value of the setting with the given key.
31
32        """
33        settings = carb.settings.get_settings()
34        return settings.get_as_string(f"exts/omni.services.example.viewport_capture.core/{setting_key}")
35
36    def test_extension_name_matches_default_extension_name(self) -> None:
37        """Ensure the method to obtain the extension's name matches the actual extension name."""
38        self.assertEqual(get_extension_name(), "omni.services.example.viewport_capture.core")
39
40    def test_captured_image_directory_ends_with_configured_setting_value(self) -> None:
41        """Ensure the resolved directory where captures are stored ends with the configured setting value."""
42        capture_directory_name = self._get_setting("capture_directory")
43
44        self.assertTrue(
45            get_captured_image_directory().endswith(capture_directory_name),
46            "Expected the path to the capture directory to end with the name of the directory provided as setting.",
47        )
48
49    def test_captured_image_path_starts_with_api_url_prefix(self) -> None:
50        """Ensure the web-friendly capture path contains the API URL components."""
51        url_prefix = self._get_setting("url_prefix")
52        capture_path = self._get_setting("capture_path")
53
54        self.assertTrue(
55            get_captured_image_path().startswith(url_prefix),
56            "Expected the web-friendly capture path to contain the API URL prefix.",
57        )
58        self.assertTrue(
59            get_captured_image_path().endswith(capture_path),
60            "Expected the web-friendly capture path to contain the API storage path.",
61        )
62
63    # [...]

Executing tests within an Omniverse application

To execute the Extension’s unit tests, you may launch an instance of an Omniverse application, where the extension has been mapped and enabled.

Once launched, you may navigate the UI of the instance to execute the unit tests bundled along with the extension:

  1. From the top-level menu bar, select Window > Extensions.

  2. From the Extension Manager window, locate the Extension by typing its name, omni.services.example.viewport_capture.core.

  3. From the list of results, select the extension, then from the menu located below its header, select the Tests section.

  4. On the test section, select any option you may be interested in selecting, then launch the execution of the tests by clicking the Run Extension Tests button.

  5. Assuming all extension tests were performed successfully, a green checkmark should indicate a successful operation. In case of failure, consult the logs for details about the tests failures.

Note

You may be interested in knowing statistics about code coverage of your extension, in order to gain better insights about the features of the codebase which are exercised by the unit tests. This can provide valuable information about the critical code sections that have been visited by tests, and which may benefit from additional scrutiny.

To generate a web-friendly code coverage report at the end of the tests, select the generate coverage report option from the list of available test features.

With our Service seeing an increased level of maturity thanks to a number of unit tests, we can now be more confident about our ability to reliably serve a growing number of Users. Let’s now see how we can facilitate deploying and scaling our Service by shipping it as a Docker container.

 
« Previous section: Capturing screenshots Next section: Containerizing the service »