Use mock_platform for device_tracker entity component tests instead of hass.components (#114398)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Jan-Philipp Benecke 2024-03-29 02:23:21 +01:00 committed by GitHub
parent 282cbfc048
commit 530552b4f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 158 additions and 141 deletions

View File

@ -1,16 +1,18 @@
"""Fixtures for component testing.""" """Fixtures for component testing."""
from collections.abc import Generator from collections.abc import Callable, Generator
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import pytest import pytest
from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from tests.common import MockToggleEntity from tests.common import MockToggleEntity
if TYPE_CHECKING: if TYPE_CHECKING:
from tests.components.device_tracker.common import MockScanner
from tests.components.light.common import MockLight from tests.components.light.common import MockLight
from tests.components.sensor.common import MockSensor from tests.components.sensor.common import MockSensor
@ -137,3 +139,21 @@ def mock_toggle_entities() -> list[MockToggleEntity]:
from tests.components.switch.common import get_mock_toggle_entities from tests.components.switch.common import get_mock_toggle_entities
return get_mock_toggle_entities() return get_mock_toggle_entities()
@pytest.fixture
def mock_legacy_device_scanner() -> "MockScanner":
"""Return mocked legacy device scanner entity."""
from tests.components.device_tracker.common import MockScanner
return MockScanner()
@pytest.fixture
def mock_legacy_device_tracker_setup() -> (
Callable[[HomeAssistant, "MockScanner"], None]
):
"""Return setup callable for legacy device tracker setup."""
from tests.components.device_tracker.common import mock_legacy_device_tracker_setup
return mock_legacy_device_tracker_setup

View File

@ -1,5 +1,6 @@
"""The tests device sun light trigger component.""" """The tests device sun light trigger component."""
from collections.abc import Callable
from datetime import datetime from datetime import datetime
from unittest.mock import patch from unittest.mock import patch
@ -26,20 +27,24 @@ from homeassistant.core import CoreState, HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed, setup_test_component_platform
from tests.components.device_tracker.common import MockScanner
from tests.components.light.common import MockLight
@pytest.fixture @pytest.fixture
async def scanner(hass, enable_custom_integrations): async def scanner(
hass: HomeAssistant,
mock_light_entities: list[MockLight],
mock_legacy_device_scanner: MockScanner,
mock_legacy_device_tracker_setup: Callable[[HomeAssistant, MockScanner], None],
) -> None:
"""Initialize components.""" """Initialize components."""
scanner = await getattr(hass.components, "test.device_tracker").async_get_scanner( mock_legacy_device_tracker_setup(hass, mock_legacy_device_scanner)
None, None mock_legacy_device_scanner.reset()
) mock_legacy_device_scanner.come_home("DEV1")
scanner.reset() setup_test_component_platform(hass, "light", mock_light_entities)
scanner.come_home("DEV1")
getattr(hass.components, "test.light").init()
with patch( with patch(
"homeassistant.components.device_tracker.legacy.load_yaml_config_file", "homeassistant.components.device_tracker.legacy.load_yaml_config_file",

View File

@ -15,11 +15,16 @@ from homeassistant.components.device_tracker import (
ATTR_MAC, ATTR_MAC,
DOMAIN, DOMAIN,
SERVICE_SEE, SERVICE_SEE,
DeviceScanner,
ScannerEntity,
SourceType,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.typing import GPSType from homeassistant.helpers.typing import GPSType
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from tests.common import MockPlatform, mock_platform
@callback @callback
@bind_hass @bind_hass
@ -51,3 +56,97 @@ def async_see(
if attributes: if attributes:
data[ATTR_ATTRIBUTES] = attributes data[ATTR_ATTRIBUTES] = attributes
hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_SEE, data)) hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_SEE, data))
class MockScannerEntity(ScannerEntity):
"""Test implementation of a ScannerEntity."""
def __init__(self):
"""Init."""
self.connected = False
self._hostname = "test.hostname.org"
self._ip_address = "0.0.0.0"
self._mac_address = "ad:de:ef:be:ed:fe"
@property
def source_type(self):
"""Return the source type, eg gps or router, of the device."""
return SourceType.ROUTER
@property
def battery_level(self):
"""Return the battery level of the device.
Percentage from 0-100.
"""
return 100
@property
def ip_address(self) -> str:
"""Return the primary ip address of the device."""
return self._ip_address
@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return self._mac_address
@property
def hostname(self) -> str:
"""Return hostname of the device."""
return self._hostname
@property
def is_connected(self):
"""Return true if the device is connected to the network."""
return self.connected
def set_connected(self):
"""Set connected to True."""
self.connected = True
self.async_write_ha_state()
class MockScanner(DeviceScanner):
"""Mock device scanner."""
def __init__(self):
"""Initialize the MockScanner."""
self.devices_home = []
def come_home(self, device):
"""Make a device come home."""
self.devices_home.append(device)
def leave_home(self, device):
"""Make a device leave the house."""
self.devices_home.remove(device)
def reset(self):
"""Reset which devices are home."""
self.devices_home = []
def scan_devices(self):
"""Return a list of fake devices."""
return list(self.devices_home)
def get_device_name(self, device):
"""Return a name for a mock device.
Return None for dev1 for testing.
"""
return None if device == "DEV1" else device.lower()
def mock_legacy_device_tracker_setup(
hass: HomeAssistant, legacy_device_scanner: MockScanner
) -> None:
"""Mock legacy device tracker platform setup."""
async def _async_get_scanner(hass, config) -> MockScanner:
"""Return the test scanner."""
return legacy_device_scanner
mocked_platform = MockPlatform()
mocked_platform.async_get_scanner = _async_get_scanner
mock_platform(hass, "test.device_tracker", mocked_platform)

View File

@ -31,6 +31,7 @@ from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import common from . import common
from .common import MockScanner, mock_legacy_device_tracker_setup
from tests.common import ( from tests.common import (
assert_setup_component, assert_setup_component,
@ -58,6 +59,14 @@ def mock_yaml_devices(hass):
os.remove(yaml_devices) os.remove(yaml_devices)
@pytest.fixture(autouse=True)
def _mock_legacy_device_tracker_setup(
hass: HomeAssistant, mock_legacy_device_scanner: MockScanner
) -> None:
"""Mock legacy device tracker setup."""
mock_legacy_device_tracker_setup(hass, mock_legacy_device_scanner)
async def test_is_on(hass: HomeAssistant) -> None: async def test_is_on(hass: HomeAssistant) -> None:
"""Test is_on method.""" """Test is_on method."""
entity_id = f"{const.DOMAIN}.test" entity_id = f"{const.DOMAIN}.test"
@ -99,9 +108,7 @@ async def test_reading_broken_yaml_config(hass: HomeAssistant) -> None:
assert res[0].dev_id == "my_device" assert res[0].dev_id == "my_device"
async def test_reading_yaml_config( async def test_reading_yaml_config(hass: HomeAssistant, yaml_devices) -> None:
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
"""Test the rendering of the YAML configuration.""" """Test the rendering of the YAML configuration."""
dev_id = "test" dev_id = "test"
device = legacy.Device( device = legacy.Device(
@ -179,9 +186,7 @@ async def test_duplicate_mac_dev_id(mock_warning, hass: HomeAssistant) -> None:
assert "Duplicate device IDs" in args[0], "Duplicate device IDs warning expected" assert "Duplicate device IDs" in args[0], "Duplicate device IDs warning expected"
async def test_setup_without_yaml_file( async def test_setup_without_yaml_file(hass: HomeAssistant, yaml_devices) -> None:
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
"""Test with no YAML file.""" """Test with no YAML file."""
with assert_setup_component(1, device_tracker.DOMAIN): with assert_setup_component(1, device_tracker.DOMAIN):
assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM) assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM)
@ -280,13 +285,11 @@ async def test_discover_platform_missing_platform(
async def test_update_stale( async def test_update_stale(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None, mock_legacy_device_scanner: MockScanner,
) -> None: ) -> None:
"""Test stalled update.""" """Test stalled update."""
mock_legacy_device_scanner.reset()
scanner = getattr(hass.components, "test.device_tracker").SCANNER mock_legacy_device_scanner.come_home("DEV1")
scanner.reset()
scanner.come_home("DEV1")
now = dt_util.utcnow() now = dt_util.utcnow()
register_time = datetime(now.year + 1, 9, 15, 23, tzinfo=dt_util.UTC) register_time = datetime(now.year + 1, 9, 15, 23, tzinfo=dt_util.UTC)
@ -313,7 +316,7 @@ async def test_update_stale(
assert hass.states.get("device_tracker.dev1").state == STATE_HOME assert hass.states.get("device_tracker.dev1").state == STATE_HOME
scanner.leave_home("DEV1") mock_legacy_device_scanner.leave_home("DEV1")
with patch( with patch(
"homeassistant.components.device_tracker.legacy.dt_util.utcnow", "homeassistant.components.device_tracker.legacy.dt_util.utcnow",
@ -328,7 +331,6 @@ async def test_update_stale(
async def test_entity_attributes( async def test_entity_attributes(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None: ) -> None:
"""Test the entity attributes.""" """Test the entity attributes."""
devices = mock_device_tracker_conf devices = mock_device_tracker_conf
@ -362,9 +364,7 @@ async def test_entity_attributes(
@patch("homeassistant.components.device_tracker.legacy.DeviceTracker.async_see") @patch("homeassistant.components.device_tracker.legacy.DeviceTracker.async_see")
async def test_see_service( async def test_see_service(mock_see, hass: HomeAssistant) -> None:
mock_see, hass: HomeAssistant, enable_custom_integrations: None
) -> None:
"""Test the see service with a unicode dev_id and NO MAC.""" """Test the see service with a unicode dev_id and NO MAC."""
with assert_setup_component(1, device_tracker.DOMAIN): with assert_setup_component(1, device_tracker.DOMAIN):
assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM) assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM)
@ -395,7 +395,6 @@ async def test_see_service(
async def test_see_service_guard_config_entry( async def test_see_service_guard_config_entry(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None: ) -> None:
"""Test the guard if the device is registered in the entity registry.""" """Test the guard if the device is registered in the entity registry."""
mock_entry = Mock() mock_entry = Mock()
@ -416,7 +415,6 @@ async def test_see_service_guard_config_entry(
async def test_new_device_event_fired( async def test_new_device_event_fired(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None: ) -> None:
"""Test that the device tracker will fire an event.""" """Test that the device tracker will fire an event."""
with assert_setup_component(1, device_tracker.DOMAIN): with assert_setup_component(1, device_tracker.DOMAIN):
@ -451,7 +449,6 @@ async def test_new_device_event_fired(
async def test_duplicate_yaml_keys( async def test_duplicate_yaml_keys(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None: ) -> None:
"""Test that the device tracker will not generate invalid YAML.""" """Test that the device tracker will not generate invalid YAML."""
devices = mock_device_tracker_conf devices = mock_device_tracker_conf
@ -471,7 +468,6 @@ async def test_duplicate_yaml_keys(
async def test_invalid_dev_id( async def test_invalid_dev_id(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None: ) -> None:
"""Test that the device tracker will not allow invalid dev ids.""" """Test that the device tracker will not allow invalid dev ids."""
devices = mock_device_tracker_conf devices = mock_device_tracker_conf
@ -485,9 +481,7 @@ async def test_invalid_dev_id(
assert not devices assert not devices
async def test_see_state( async def test_see_state(hass: HomeAssistant, yaml_devices) -> None:
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
"""Test device tracker see records state correctly.""" """Test device tracker see records state correctly."""
assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM) assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -527,7 +521,7 @@ async def test_see_state(
async def test_see_passive_zone_state( async def test_see_passive_zone_state(
hass: HomeAssistant, hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None, mock_legacy_device_scanner: MockScanner,
) -> None: ) -> None:
"""Test that the device tracker sets gps for passive trackers.""" """Test that the device tracker sets gps for passive trackers."""
now = dt_util.utcnow() now = dt_util.utcnow()
@ -547,9 +541,8 @@ async def test_see_passive_zone_state(
await async_setup_component(hass, zone.DOMAIN, {"zone": zone_info}) await async_setup_component(hass, zone.DOMAIN, {"zone": zone_info})
await hass.async_block_till_done() await hass.async_block_till_done()
scanner = getattr(hass.components, "test.device_tracker").SCANNER mock_legacy_device_scanner.reset()
scanner.reset() mock_legacy_device_scanner.come_home("dev1")
scanner.come_home("dev1")
with ( with (
patch( patch(
@ -581,7 +574,7 @@ async def test_see_passive_zone_state(
assert attrs.get("gps_accuracy") == 0 assert attrs.get("gps_accuracy") == 0
assert attrs.get("source_type") == SourceType.ROUTER assert attrs.get("source_type") == SourceType.ROUTER
scanner.leave_home("dev1") mock_legacy_device_scanner.leave_home("dev1")
with patch( with patch(
"homeassistant.components.device_tracker.legacy.dt_util.utcnow", "homeassistant.components.device_tracker.legacy.dt_util.utcnow",
@ -668,12 +661,11 @@ async def test_bad_platform(hass: HomeAssistant) -> None:
async def test_adding_unknown_device_to_config( async def test_adding_unknown_device_to_config(
mock_device_tracker_conf: list[legacy.Device], mock_device_tracker_conf: list[legacy.Device],
hass: HomeAssistant, hass: HomeAssistant,
enable_custom_integrations: None, mock_legacy_device_scanner: MockScanner,
) -> None: ) -> None:
"""Test the adding of unknown devices to configuration file.""" """Test the adding of unknown devices to configuration file."""
scanner = getattr(hass.components, "test.device_tracker").SCANNER mock_legacy_device_scanner.reset()
scanner.reset() mock_legacy_device_scanner.come_home("DEV1")
scanner.come_home("DEV1")
await async_setup_component( await async_setup_component(
hass, device_tracker.DOMAIN, {device_tracker.DOMAIN: {CONF_PLATFORM: "test"}} hass, device_tracker.DOMAIN, {device_tracker.DOMAIN: {CONF_PLATFORM: "test"}}

View File

@ -1,99 +0,0 @@
"""Provide a mock device scanner."""
from homeassistant.components.device_tracker import DeviceScanner
from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.components.device_tracker.const import SourceType
async def async_get_scanner(hass, config):
"""Return a mock scanner."""
return SCANNER
class MockScannerEntity(ScannerEntity):
"""Test implementation of a ScannerEntity."""
def __init__(self):
"""Init."""
self.connected = False
self._hostname = "test.hostname.org"
self._ip_address = "0.0.0.0"
self._mac_address = "ad:de:ef:be:ed:fe"
@property
def source_type(self):
"""Return the source type, eg gps or router, of the device."""
return SourceType.ROUTER
@property
def battery_level(self):
"""Return the battery level of the device.
Percentage from 0-100.
"""
return 100
@property
def ip_address(self) -> str:
"""Return the primary ip address of the device."""
return self._ip_address
@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return self._mac_address
@property
def hostname(self) -> str:
"""Return hostname of the device."""
return self._hostname
@property
def is_connected(self):
"""Return true if the device is connected to the network."""
return self.connected
def set_connected(self):
"""Set connected to True."""
self.connected = True
self.async_schedule_update_ha_state()
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the config entry."""
entity = MockScannerEntity()
async_add_entities([entity])
class MockScanner(DeviceScanner):
"""Mock device scanner."""
def __init__(self):
"""Initialize the MockScanner."""
self.devices_home = []
def come_home(self, device):
"""Make a device come home."""
self.devices_home.append(device)
def leave_home(self, device):
"""Make a device leave the house."""
self.devices_home.remove(device)
def reset(self):
"""Reset which devices are home."""
self.devices_home = []
def scan_devices(self):
"""Return a list of fake devices."""
return list(self.devices_home)
def get_device_name(self, device):
"""Return a name for a mock device.
Return None for dev1 for testing.
"""
return None if device == "DEV1" else device.lower()
SCANNER = MockScanner()