Testing your plugin#

HydroMT Core offers some functionalities that can help you test your plugins, as well as offering some examples on how to do it. Below are listed some tips and tricks to help test your plugin and to help your users test their code using your plugin.

Testing model components#

When you implement a ModelComponent we very strongly encourage you to write a test_equals method on your component that should test for deep equality. This means that it shouldn’t just test whether it’s literally the same python object, but should actually attempt to test whether the attributes and any data held by it are the same. This is not only important for your own testing, but also for your users, since they might want to use that functionality to test their own code.

The function should have the following signature:

def test_equal(self, other: "ModelComponent") -> tuple[bool, Dict[str, str]]:
    ...

So counter to what we told you about processes this function should take the actual model as input. The return signature is a boolean indicating whether the equality is true or not (as usual) and a dictionary. In this dictionary you can add any error messages of whatever keys you’d like. If possible, it is good for usability if you can report as many issues as possible in one go, so the user doesn’t have to run the tests over and over again.

As an example, suppose we have a RainFallComponent that must have a time dimension, and x dimension, and additionally the data inside it must have the correct crs. A function for that might look like this:

class RainFallComponent(ModelComponent):

    def test_equal(self, other: "ModelComponent") -> tuple[bool, Dict[str, str]]:
        errors: Dict[str, str] = {}
        if not isinstance(other, self.__class__):
            errors['type'] = """Other is not of type RainFallComponent and therfore
            cannot be equal"""
            return (False, errors)

        if not 'time' in other.dims().keys():
            errors['no_time_dim'] = """Component does not have the required time
            dimension"""
        else:
            if not self.data.sel(time=slice(None)).equals(other.data.sel(time=slice(None))):
                errors['time_dim_not_equal'] = "time dimension data is not equal"

        if not 'x' in other.dims().keys():
            errors['no_x_dim'] = """Component does not have the required x
            dimension"""
        else:
            if not self.data.sel(x=slice(None)).equals(other.data.sel(x=slice(None))):
                errors['x_dim_not_equal'] = "x dimension data is not equal"

        return len(errors) == 0, errors

Note that in the case the classes are not equal we return early since it probably doesn’t make sense to test for data equality on random classes. However, in the other cases we check both the time and x dimension at the same time. This gives users as much information about what is wrong as possible.

Testing models#

If all the components you use have a test_equal function defined than testing for model equality should be relatively simple. The core base model also has a test_equal function defined that will test all the components against each other so if that is all you require you can simply use that function. If you wish to do additional checks you can override this method and simply call super().test_equals(other) and do whatever checks you’d like after that.

Testing your plugin as a whole#

Depending on how up to date with core developments you’d like to be it might be good to test against both the latest released version of hydromt core (which is presumably what your users will be using) as well as the latest version of Hydromt on the main branch (the development version as it were). This can help you anticipate if core might release features in the future that are incompatible for you and fix problems before they arise for your users. This also gives you the opportunity to file bug reports with core to fix things before they are released, which we highly encourage! If you use an environment/package manager that supports this such as pixi then you can do this by making a separate optional dependency-group for it, and simply run the test suite against the different environments in your CI.