HelloThisIsFlo/Appdaemon-Test-Framework

Can't test inherited apps

bachya opened this issue · 4 comments

It appears that inherited AppDaemon apps cannot be tested. For example, consider a case where a subclass calls listen_event:

apps/base.py

from appdaemon.plugins.hass.hassapi import Hass  # type: ignore


class Base(Hass):
    """Define a base automation object."""

    def initialize(self):
        """Initialize."""
        # Do some stuff...

apps/subclass.py

from .base import Base


class Subclass(Base):
    """Define automated alterations to vacation mode."""

    def initialize(self) -> None:
        """Initialize."""
        super().initialize()

       self.listen_event(self.callback, 'EVENT_NAME')  

    def callback(self, event_name, data, kwargs):
        """Run the callback."""
        # Do some stuff...

If I were to write a simple test of Subclass:

tests/test_subclass.py

import pytest

from ..apps.hass import AutoVacationMode


@pytest.fixture
def subclass(given_that):
    """Define a fixture for Subclass."""
    app = Subclass(None, None, None, None, None, None, None, None)
    app.initialize()
    given_that.mock_functions_are_cleared()
    return app


def test_subclass(hass_functions, subclass):
    """Test."""
    listen_event = hass_functions['listen_event']
    listen_event.assert_any_call(subclass.callback, 'EVENT_NAME')

...I am greeted with a stacktrace that looks like this:

>   ???
E   AssertionError: listen_event(<bound method Subclass.callback of <settings.apps.hass.Subclass object at 0x108abb908>>, 'EVENT_NAME') call not found

Could totally be due to my incompetence with pytest, but would appreciate thoughts.

Turned out to be a my own fault. Closing!

I see you solved the problem. But if anyone else is reading this, or if you found another solution, here's what's happening:

  • The call being checked is in initialize
  • But to check for calls located in initialize, initialized must be called explicitly in the test
  • The reason is the given_that.mock_functions_are_cleared() in the initialization fixture will clear any existing calls.
    • This is intentional and is intended to provide a clean slate at the beginning of each test, improving readbility

Was it the source of your problem? Or you solved it another way?

Bingo, you've got it!

Awesome! Thanks for your feedback 👍