Skip to content

Commit

Permalink
Added contributor docs for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
swainn committed Jan 3, 2025
1 parent 2e686d1 commit 6ad9a29
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 20 deletions.
2 changes: 2 additions & 0 deletions docs/contribute/dev_environment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ The manual setup process involves cloning a copy of the Tethys Platform source c
Other Common Development Setups
===============================

.. _setup_dev_environment_postgis:

Use PostGIS Running in Docker
-----------------------------

Expand Down
144 changes: 124 additions & 20 deletions docs/contribute/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,151 @@
Testing
*******

**Last Updated:** December 2024

Testing Requirements
====================

* Coverage
* Code Style
**Last Updated:** January 2024

Setup Testing Environment
=========================

The Tethys Platform test suite requires a PostgreSQL database and Tethys must be configured with a database user that has superuser privileges so that it can create and delete the test database automatically. The following steps will guide you through setting up a testing environment for Tethys Platform:

1. Setup a development installation of Tethys Platform by following the :ref:`setup_dev_environment` tutorial.
2. Be sure to set up your development environment to :ref:`setup_dev_environment_postgis`.
3. Configure Tethys Platform to use the ``tethys_super`` user for the database connection by editing the :file:`portal_config.yml`:

Running Tests Locally
=====================
.. code-block:: yaml
settings:
DATABASES:
default:
ENGINE: django.db.backends.postgresql
NAME: tethys_platform
USER: tethys_super
PASSWORD: pass
HOST: localhost
PORT: <DB_PORT>
Your development environment should be ready for running the test suite.

Writing Tests
Running Tests
=============

When you are developing, you will need to write tests and run them locally to verify that they work. At the time of writing, Tethys Platform only had unit tests for its Python code. To run the entire Python unit test suite, use the following command:

.. code-block:: bash
tethys test -cu
The ``-u`` flag selects the Unit test suite to run and he ``-c`` flag causes the coverage report to be printed after testing has completed. The coverage report will show you which lines of code need more tests coverage. Tethys Platform requires 100% test coverage for all new code.

Running the entire suite can take a long time, so you may want to run a subset of the tests. Use the ``-f`` flag to run a specific test file or directory, giving it the path relative to the CWD directory or the absolute path. For example, to run the tests for the ``harvester.py`` file from the repository root directory, you could use the following command:

.. code-block:: bash
tethys test -f tests/unit_tests/test_tethys_apps/test_harvester.py
Code Style
==========

The Python code in Tethys Platform is developed following the `PEP8 style guide <https://peps.python.org/pep-0008/>`_. The code is linted using flake8 and formatted using the Black code formatter.

flake8
------

Tethys Platform uses the flake8 linter to check for conformance to the PEP8 style guide and other Python codestyle best practices. To run the linter, run the following command in the root of the Tethys Platform repository:

.. code-block:: bash
flake8
If no output is displayed, the check passed and there are now issues. If any issues are found, they will be listed in the output. Most are self explanatory, but a quick web search referencing the issue code will usually provide more information on the issue and how to resolve it. For more information on flake8, see the `flake8 documentation <https://flake8.pycqa.org/en/latest/>`_.

Black
-----

Within the PEP8 style guide, there is a lot of room for interpretation for how code can be formatted. This can lead to inconsistencies in code style across a large codebase. To help maintain a consistent code style, Tethys Platform uses the Black code formatter. Using a formatter like Black can help reduce the time spent on code reviews by minimizing the diffs and preventing tiffs over how a block of code should be styled.

You will most frequently encounter the need to run the Black formatter after the Black check fails on a Pull Request you have opened. Fixing the failure is simple: run Black on the codebase, then commit and push the changes. To format the code using Black, run the following command in the root of the Tethys Platform repository:

.. code-block:: bash
black
To learn more about the Black code formatter, see the `Black documentation <https://black.readthedocs.io/en/stable/>`_.

Backend Unit Tests
------------------
Writing Tests
=============

Whether you are adding a new feature or fixing a bug, you should write tests to verify that the code works as expected. This may involve updating existing tests or writing new ones. The following sections provide guidance on writing tests for Python and JavaScript code.

Python Unit Tests
-----------------

The Python tests are written using the `unittest <https://docs.python.org/3/library/unittest.html>`_ framework and

Frontend Unit Tests
-------------------

Organization
++++++++++++

The Python tests are located in the :file:`tests` directory at the root of the repository. The tests are organized into subdirectories based on the module they are testing. For example, tests for the ``tethys_apps.harvester`` module are located in the :file:`tests/unit_tests/test_tethys_apps/test_harvester.py` file. This pattern is used to make finding tests easier and should be followed when adding new test files.

Mocking
-------
+++++++

As unit tests, the Python tests should be focused on testing the smallest units of code possible. This means that you should mock out any external service dependencies that are not the focus of the test such as GeoServer or HTCondor. When the tests are run during the GitHub action checks, these services won't be avaialble. The exception to this is the primary Tethys Platform database, which may be used in tests and will be available for checks (see below).

The `unittest.mock <https://docs.python.org/3/library/unittest.mock.html>`_ module is used to create mock objects in place of services or third-party library objects. The mock objects can be used to simulate the behavior of the real objects and control the return values of methods. For example, to mock the ``requests.get`` function, you could use the following code:

Code Style
==========
.. code-block:: python
from unittest.mock import patch
@patch('some_module.that_uses.requests.get')
def test_my_function(mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {'key': 'value'}
result = my_function()
assert result == 'value'
There are many tutorials and guides available online that can help you learn how to use the ``unittest.mock`` module effectively, so it won't be covered in detail here. There are also many examples in the 2000+ existing tests in the Tethys Platform codebase that you can use as a reference.

Database
++++++++

Some tests need to interact with the database to verify that the code is working as expected. Most often this is the case when the code makes uses of one of the many Django ORM models (e.g. tethys_apps.models). Tests that interact with the database should use the ``TethysTestCase``, which inherits from the Django ``TestCase`` class. This class is able to use the test database that is created for tests. It also provides special setup and tear down functionality that ensures that the tests are isolated from each other and that the database is in a known state when the test starts.

Consider this example from :file:`tests/unit_tests/test_tethys_apps/test_models/test_TethysApp.py`:

.. code-block:: python
from tethys_sdk.testing import TethysTestCase
from tethys_apps.models import (
TethysApp,
TethysAppSetting,
)
class TethysAppTests(TethysTestCase):
def set_up(self):
self.test_app = TethysApp.objects.get(package="test_app")
def tear_down(self):
self.test_app.delete()
def test_add_settings(self):
new_setting = TethysAppSetting(name="new_setting", required=False)
self.test_app.add_settings([new_setting])
app = TethysApp.objects.get(package="test_app")
settings = app.settings_set.filter(name="new_setting")
self.assertEqual(1, len(settings))
The ``test_add_settings`` method tests the ``add_settings`` method of the ``TethysApp`` Django model. The test creates a new ``TethysAppSetting``, adds it to the app, and then verifies that the setting was added to the database. The test uses the ``TethysTestCase`` class to ensure that the test database is availalbe for the test.

There are many examples of tests that interact with the database that can be found with a project-wide search for ``TethysTestCase``.

JavaScript Unit Tests
---------------------

* PEP8
* flake8
* Code formatter
Coming Soon

0 comments on commit 6ad9a29

Please sign in to comment.