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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/114435/head
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."""
from collections.abc import Generator
from collections.abc import Callable, Generator
from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock, patch
import pytest
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from tests.common import MockToggleEntity
if TYPE_CHECKING:
from tests.components.device_tracker.common import MockScanner
from tests.components.light.common import MockLight
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
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."""
from collections.abc import Callable
from datetime import datetime
from unittest.mock import patch
@ -26,20 +27,24 @@ from homeassistant.core import CoreState, HomeAssistant
from homeassistant.setup import async_setup_component
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
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."""
scanner = await getattr(hass.components, "test.device_tracker").async_get_scanner(
None, None
)
mock_legacy_device_tracker_setup(hass, mock_legacy_device_scanner)
mock_legacy_device_scanner.reset()
mock_legacy_device_scanner.come_home("DEV1")
scanner.reset()
scanner.come_home("DEV1")
getattr(hass.components, "test.light").init()
setup_test_component_platform(hass, "light", mock_light_entities)
with patch(
"homeassistant.components.device_tracker.legacy.load_yaml_config_file",

View File

@ -15,11 +15,16 @@ from homeassistant.components.device_tracker import (
ATTR_MAC,
DOMAIN,
SERVICE_SEE,
DeviceScanner,
ScannerEntity,
SourceType,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.typing import GPSType
from homeassistant.loader import bind_hass
from tests.common import MockPlatform, mock_platform
@callback
@bind_hass
@ -51,3 +56,97 @@ def async_see(
if attributes:
data[ATTR_ATTRIBUTES] = attributes
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
from . import common
from .common import MockScanner, mock_legacy_device_tracker_setup
from tests.common import (
assert_setup_component,
@ -58,6 +59,14 @@ def mock_yaml_devices(hass):
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:
"""Test is_on method."""
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"
async def test_reading_yaml_config(
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
async def test_reading_yaml_config(hass: HomeAssistant, yaml_devices) -> None:
"""Test the rendering of the YAML configuration."""
dev_id = "test"
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"
async def test_setup_without_yaml_file(
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
async def test_setup_without_yaml_file(hass: HomeAssistant, yaml_devices) -> None:
"""Test with no YAML file."""
with assert_setup_component(1, device_tracker.DOMAIN):
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(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
mock_legacy_device_scanner: MockScanner,
) -> None:
"""Test stalled update."""
scanner = getattr(hass.components, "test.device_tracker").SCANNER
scanner.reset()
scanner.come_home("DEV1")
mock_legacy_device_scanner.reset()
mock_legacy_device_scanner.come_home("DEV1")
now = dt_util.utcnow()
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
scanner.leave_home("DEV1")
mock_legacy_device_scanner.leave_home("DEV1")
with patch(
"homeassistant.components.device_tracker.legacy.dt_util.utcnow",
@ -328,7 +331,6 @@ async def test_update_stale(
async def test_entity_attributes(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None:
"""Test the entity attributes."""
devices = mock_device_tracker_conf
@ -362,9 +364,7 @@ async def test_entity_attributes(
@patch("homeassistant.components.device_tracker.legacy.DeviceTracker.async_see")
async def test_see_service(
mock_see, hass: HomeAssistant, enable_custom_integrations: None
) -> None:
async def test_see_service(mock_see, hass: HomeAssistant) -> None:
"""Test the see service with a unicode dev_id and NO MAC."""
with assert_setup_component(1, device_tracker.DOMAIN):
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(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None:
"""Test the guard if the device is registered in the entity registry."""
mock_entry = Mock()
@ -416,7 +415,6 @@ async def test_see_service_guard_config_entry(
async def test_new_device_event_fired(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None:
"""Test that the device tracker will fire an event."""
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(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None:
"""Test that the device tracker will not generate invalid YAML."""
devices = mock_device_tracker_conf
@ -471,7 +468,6 @@ async def test_duplicate_yaml_keys(
async def test_invalid_dev_id(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
) -> None:
"""Test that the device tracker will not allow invalid dev ids."""
devices = mock_device_tracker_conf
@ -485,9 +481,7 @@ async def test_invalid_dev_id(
assert not devices
async def test_see_state(
hass: HomeAssistant, yaml_devices, enable_custom_integrations: None
) -> None:
async def test_see_state(hass: HomeAssistant, yaml_devices) -> None:
"""Test device tracker see records state correctly."""
assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM)
await hass.async_block_till_done()
@ -527,7 +521,7 @@ async def test_see_state(
async def test_see_passive_zone_state(
hass: HomeAssistant,
mock_device_tracker_conf: list[legacy.Device],
enable_custom_integrations: None,
mock_legacy_device_scanner: MockScanner,
) -> None:
"""Test that the device tracker sets gps for passive trackers."""
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 hass.async_block_till_done()
scanner = getattr(hass.components, "test.device_tracker").SCANNER
scanner.reset()
scanner.come_home("dev1")
mock_legacy_device_scanner.reset()
mock_legacy_device_scanner.come_home("dev1")
with (
patch(
@ -581,7 +574,7 @@ async def test_see_passive_zone_state(
assert attrs.get("gps_accuracy") == 0
assert attrs.get("source_type") == SourceType.ROUTER
scanner.leave_home("dev1")
mock_legacy_device_scanner.leave_home("dev1")
with patch(
"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(
mock_device_tracker_conf: list[legacy.Device],
hass: HomeAssistant,
enable_custom_integrations: None,
mock_legacy_device_scanner: MockScanner,
) -> None:
"""Test the adding of unknown devices to configuration file."""
scanner = getattr(hass.components, "test.device_tracker").SCANNER
scanner.reset()
scanner.come_home("DEV1")
mock_legacy_device_scanner.reset()
mock_legacy_device_scanner.come_home("DEV1")
await async_setup_component(
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()