Skip to content

Commit

Permalink
Merge pull request #154 from agittins/fix-tests
Browse files Browse the repository at this point in the history
commit 62e3449 (HEAD -> fix-tests, origin/fix-tests)
Author: Ashley Gittins <ash@ajg.net.au>
Date:   Sat Apr 6 16:28:13 2024 +0000

    tests: fix tests erroring out
    
    - reduce coverage requirement to 43% (now at 43.69%)
    - add fixture for bluetooth mocking
    - initial (incomplete) attempt at patching service_infos for options flow
    - disable for now test for ConfigEntryNotReady exception.

commit 683732a
Author: Ashley Gittins <ash@ajg.net.au>
Date:   Fri Apr 5 16:23:06 2024 +0000

    Initial improvements to test coverage 40.48%
    
    - started at 12% or so.
  • Loading branch information
agittins authored Apr 6, 2024
2 parents f379d28 + 62e3449 commit 847e9c6
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 69 deletions.
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
asyncio_mode = auto
7 changes: 6 additions & 1 deletion scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
# Install requirements
pip install -r requirements_test.txt
# Run tests and get a summary of successes/failures and code coverage
pytest --durations=10 --cov-report term-missing --cov=custom_components.bermuda tests
pytest --durations=10 --cov-report term-missing --cov=custom_components.bermuda tests --cov-fail-under=43

# Note that the github workflow runs:
#
# pytest --timeout=9 --durations=10 -n auto -p no:sugar tests
# which doesn't care about coverage.
43 changes: 34 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@

import pytest

from .const import SERVICE_INFOS

# from custom_components.bermuda import BermudaDataUpdateCoordinator


pytest_plugins = "pytest_homeassistant_custom_component"


# This fixture enables loading custom integrations in all tests.
# Remove to enable selective use of this fixture
@pytest.fixture(autouse=True)
def auto_enable_custom_integrations(enable_custom_integrations):
"""Enable loading custom integrations."""
yield


# This fixture is used to prevent HomeAssistant from
# attempting to create and dismiss persistent
# notifications. These calls would fail without this
Expand All @@ -30,9 +43,8 @@ def skip_notifications_fixture():
@pytest.fixture(name="bypass_get_data")
def bypass_get_data_fixture():
"""Skip calls to get data from API."""
# AJG: We don't implement this, so nothing to monkeypatch.
# with patch("custom_components.bermuda.BermudaApiClient.async_get_data"):
# yield
with patch("custom_components.bermuda.BermudaDataUpdateCoordinator.async_refresh"):
yield


# In this fixture, we are forcing calls to async_get_data to raise
Expand All @@ -41,9 +53,22 @@ def bypass_get_data_fixture():
@pytest.fixture(name="error_on_get_data")
def error_get_data_fixture():
"""Simulate error when retrieving data from API."""
# AJG: again we don't implement async_get_data...
# with patch(
# "custom_components.bermuda.BermudaApiClient.async_get_data",
# side_effect=Exception,
# ):
# yield
with patch(
"custom_components.bermuda.BermudaDataUpdateCoordinator.async_refresh",
side_effect=Exception,
):
yield


@pytest.fixture(autouse=True)
def mock_bluetooth(enable_bluetooth):
"""Auto mock bluetooth."""


# This fixture ensures that the config flow gets service info for the anticipated address
# to go into configured_devices
@pytest.fixture(autouse=True)
def mock_service_info():
"""Simulate a discovered advertisement for config_flow"""
with patch("custom_components.bermuda.bluetooth.async_discovered_service_info"):
return SERVICE_INFOS
43 changes: 34 additions & 9 deletions tests/const.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
"""Constants for Bermuda BLE Trilateration tests."""

# AJG: We don't use these vars in our flow. TODO: Add some we _do_ use!
# from custom_components.bermuda.const import (
# CONF_PASSWORD,
# )
# from custom_components.bermuda.const import (
# CONF_USERNAME,
# )
# MOCK_CONFIG = {CONF_USERNAME: "test_username", CONF_PASSWORD: "test_password"}
from __future__ import annotations

MOCK_CONFIG = {}
import custom_components.bermuda.const

# from custom_components.bermuda.const import CONF_DEVICES
# from custom_components.bermuda.const import CONF_MAX_RADIUS


MOCK_OPTIONS = {
custom_components.bermuda.const.CONF_MAX_RADIUS: 20.0,
custom_components.bermuda.const.CONF_MAX_VELOCITY: 3.0,
custom_components.bermuda.const.CONF_DEVTRACK_TIMEOUT: 30,
custom_components.bermuda.const.CONF_UPDATE_INTERVAL: 10.0,
custom_components.bermuda.const.CONF_SMOOTHING_SAMPLES: 20,
custom_components.bermuda.const.CONF_ATTENUATION: 3.0,
custom_components.bermuda.const.CONF_REF_POWER: -55.0,
custom_components.bermuda.const.CONF_DEVICES: [], # ["EE:E8:37:9F:6B:54"],
}

MOCK_CONFIG = {"source": "user"}


SERVICE_INFOS = [
{
"name": "test device",
"advertisement": {"local_name": "test local name"},
"device": {"name": "test device name"},
"address": "EE:E8:37:9F:6B:54",
},
{
"name": "test device2",
"advertisement": {"local_name": "test local name2"},
"device": {"name": "test device name2"},
"address": "EE:E8:37:9F:6B:56",
},
]
23 changes: 11 additions & 12 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
from homeassistant import data_entry_flow
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda.const import BINARY_SENSOR
from custom_components.bermuda.const import DOMAIN
from custom_components.bermuda.const import PLATFORMS
from custom_components.bermuda.const import SENSOR
from custom_components.bermuda.const import SWITCH
from custom_components.bermuda.const import NAME

from .const import MOCK_CONFIG
from .const import MOCK_OPTIONS


# This fixture bypasses the actual setup of the integration
Expand Down Expand Up @@ -57,8 +55,9 @@ async def test_successful_config_flow(hass, bypass_get_data):
# Check that the config flow is complete and a new entry is created with
# the input data
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "test_username"
assert result["data"] == MOCK_CONFIG
assert result["title"] == NAME
assert result["data"] == {"source": "user"}
assert result["options"] == {}
assert result["result"]


Expand All @@ -80,8 +79,8 @@ async def test_failed_config_flow(hass, error_on_get_data):
result["flow_id"], user_input=MOCK_CONFIG
)

assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "auth"}
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
assert result.get("errors") is None


# Our config flow also has an options flow, so we must test it as well.
Expand All @@ -98,17 +97,17 @@ async def test_options_flow(hass):

# Verify that the first options step is a user form
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
assert result["step_id"] == "globalopts"

# Enter some fake data into the form
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={platform: platform != SENSOR for platform in PLATFORMS},
user_input=MOCK_OPTIONS,
)

# Verify that the flow finishes
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "test_username"
assert result["title"] == NAME

# Verify that the options were updated
assert entry.options == {BINARY_SENSOR: True, SENSOR: False, SWITCH: True}
assert entry.options == MOCK_OPTIONS
22 changes: 14 additions & 8 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

from __future__ import annotations

import pytest
from homeassistant.exceptions import ConfigEntryNotReady
# from homeassistant.exceptions import ConfigEntryNotReady
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda import BermudaDataUpdateCoordinator
Expand Down Expand Up @@ -34,15 +33,15 @@ async def test_setup_unload_and_reload_entry(hass, bypass_get_data):
# call, no code from custom_components/bermuda/api.py actually runs.
assert await async_setup_entry(hass, config_entry)
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
assert type(hass.data[DOMAIN][config_entry.entry_id]).isinstance(
BermudaDataUpdateCoordinator
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id], BermudaDataUpdateCoordinator
)

# Reload the entry and assert that the data from above is still there
assert await async_reload_entry(hass, config_entry) is None
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
assert type(hass.data[DOMAIN][config_entry.entry_id]).isinstance(
BermudaDataUpdateCoordinator
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id], BermudaDataUpdateCoordinator
)

# Unload the entry and verify that the data has been removed
Expand All @@ -54,8 +53,15 @@ async def test_setup_entry_exception(hass, error_on_get_data):
"""Test ConfigEntryNotReady when API raises an exception during entry setup."""
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")

assert config_entry is not None

# In this case we are testing the condition where async_setup_entry raises
# ConfigEntryNotReady using the `error_on_get_data` fixture which simulates
# an error.
with pytest.raises(ConfigEntryNotReady):
assert await async_setup_entry(hass, config_entry)

# Hmmm... this doesn't seem to be how this works. The super's _async_refresh might
# handle exceptions, in which it then sets self.last_update_status, which is what
# async_setup_entry checks in order to raise ConfigEntryNotReady, but I don't think
# anything will "catch" our over-ridded async_refresh's exception.
# with pytest.raises(ConfigEntryNotReady):
# assert await async_setup_entry(hass, config_entry)
63 changes: 33 additions & 30 deletions tests/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@

from __future__ import annotations

from unittest.mock import call
from unittest.mock import patch

from homeassistant.components.switch import SERVICE_TURN_OFF
from homeassistant.components.switch import SERVICE_TURN_ON
from homeassistant.const import ATTR_ENTITY_ID
# from homeassistant.components.switch import SERVICE_TURN_OFF
# from homeassistant.components.switch import SERVICE_TURN_ON
# from homeassistant.const import ATTR_ENTITY_ID
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.bermuda import async_setup_entry
from custom_components.bermuda.const import DEFAULT_NAME

# from custom_components.bermuda.const import DEFAULT_NAME
from custom_components.bermuda.const import DOMAIN
from custom_components.bermuda.const import SWITCH

from .const import MOCK_CONFIG

# from unittest.mock import call
# from unittest.mock import patch


# from custom_components.bermuda.const import SWITCH


async def test_switch_services(hass):
"""Test switch services."""
Expand All @@ -29,25 +32,25 @@ async def test_switch_services(hass):
# in test code as well and can be used to test
# additional things, like whether a function
# was called or what arguments it was called with
with patch(
"custom_components.bermuda.BermudaApiClient.async_set_title"
) as title_func:
await hass.services.async_call(
SWITCH,
SERVICE_TURN_OFF,
service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
blocking=True,
)
assert title_func.called
assert title_func.call_args == call("foo")

title_func.reset_mock()

await hass.services.async_call(
SWITCH,
SERVICE_TURN_ON,
service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
blocking=True,
)
assert title_func.called
assert title_func.call_args == call("bar")
# with patch(
# "custom_components.bermuda.BermudaDataUpdateCoordinator.async_set_title"
# ) as title_func:
# await hass.services.async_call(
# SWITCH,
# SERVICE_TURN_OFF,
# service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
# blocking=True,
# )
# assert title_func.called
# assert title_func.call_args == call("foo")

# title_func.reset_mock()

# await hass.services.async_call(
# SWITCH,
# SERVICE_TURN_ON,
# service_data={ATTR_ENTITY_ID: f"{SWITCH}.{DEFAULT_NAME}_{SWITCH}"},
# blocking=True,
# )
# assert title_func.called
# assert title_func.call_args == call("bar")

0 comments on commit 847e9c6

Please sign in to comment.