Skip to content

Commit

Permalink
Use Syrupy for assertions
Browse files Browse the repository at this point in the history
Also, refactor to use fixtures to setup tests.
  • Loading branch information
wbyoung committed Jul 11, 2024
1 parent c2179a4 commit 0bf2f6a
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 176 deletions.
1 change: 1 addition & 0 deletions requirements.test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pre-commit==3.7.1
pytest==8.2.0
pytest-cov==5.0.0
pytest-homeassistant-custom-component==0.13.135
syrupy==4.6.1
86 changes: 83 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
"""Fixtures for testing."""

from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, patch, PropertyMock
from pathlib import Path

from homeassistant.core import HomeAssistant

import json
import pytest
from typing import Generator

from pytest_homeassistant_custom_component.common import (
MockConfigEntry,
)

from custom_components.watersmart.client import AuthenticationError
from custom_components.watersmart.const import DOMAIN

FIXTURES_DIR = Path(__file__).parent.joinpath("fixtures")


class AdvacnedPropertyMock(PropertyMock):
def __get__(self, obj, obj_type=None):
return self(obj)

def __set__(self, obj, val):
self(obj, val)


class FixtureLoader:
"""Fixture loader."""

Expand Down Expand Up @@ -78,10 +95,10 @@ def mock_aiohttp_session() -> Generator[dict[str, AsyncMock], None, None]:


@pytest.fixture
def mock_watersmart_client() -> Generator[AsyncMock, None, None]:
def mock_watersmart_client(fixture_loader) -> Generator[AsyncMock, None, None]:
"""Mock a WaterSmart client."""

hourly_data = FixtureLoader().realtime_api_response_obj["data"]["series"]
hourly_data = fixture_loader.realtime_api_response_obj["data"]["series"]

with (
patch(
Expand All @@ -105,3 +122,66 @@ def mock_watersmart_client() -> Generator[AsyncMock, None, None]:
client.async_get_hourly_data.return_value = hourly_data

yield client


@pytest.fixture
def client_authentication_error(mock_watersmart_client):
mock_watersmart_client.async_get_hourly_data.side_effect = AuthenticationError(
"invalid credentials"
)


@pytest.fixture
def mock_sensor_name() -> Generator[PropertyMock, None, None]:
"""Mock sensor names.
This testing setup/library does not use `strings.json` and the entity description to translation key
to get the entity name, so it's being patched here to just use the translaiton key. That way we at least
get entity ids that are closer to what they will really be.
"""

with patch(
"homeassistant.components.sensor.SensorEntity.name",
new_callable=AdvacnedPropertyMock,
) as mock_name:

def name_from_entity_description(sensor):
return sensor.entity_description.translation_key.replace(
"_", " "
).capitalize()

mock_name.side_effect = lambda self: (
name_from_entity_description(self) if self else None
)

yield mock_name


@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
"host": "test",
"username": "test@home-assistant.io",
"password": "Passw0rd",
},
)


@pytest.fixture
async def init_integration(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_sensor_name: Generator[PropertyMock, None, None],
mock_watersmart_client: Generator[AsyncMock, None, None],
) -> MockConfigEntry:
"""Set up the WaterSmart integration for testing."""

mock_config_entry.add_to_hass(hass)

await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()

return mock_config_entry
203 changes: 203 additions & 0 deletions tests/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# serializer version: 1
# name: test_most_recent_day_sensor
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data scraped from WaterSmart',
'device_class': 'water',
'friendly_name': 'WaterSmart (test) Gallons for most recent full day',
'related': list([
dict({
'gallons': 1,
'start': '2024-06-20T00:00:00-07:00',
}),
dict({
'gallons': 2,
'start': '2024-06-20T01:00:00-07:00',
}),
dict({
'gallons': 3,
'start': '2024-06-20T02:00:00-07:00',
}),
dict({
'gallons': 4,
'start': '2024-06-20T03:00:00-07:00',
}),
dict({
'gallons': 5,
'start': '2024-06-20T04:00:00-07:00',
}),
dict({
'gallons': 6,
'start': '2024-06-20T05:00:00-07:00',
}),
dict({
'gallons': 7,
'start': '2024-06-20T06:00:00-07:00',
}),
dict({
'gallons': 8,
'start': '2024-06-20T07:00:00-07:00',
}),
dict({
'gallons': 9,
'start': '2024-06-20T08:00:00-07:00',
}),
dict({
'gallons': 10,
'start': '2024-06-20T09:00:00-07:00',
}),
dict({
'gallons': 11,
'start': '2024-06-20T10:00:00-07:00',
}),
dict({
'gallons': 12,
'start': '2024-06-20T11:00:00-07:00',
}),
dict({
'gallons': 13,
'start': '2024-06-20T12:00:00-07:00',
}),
dict({
'gallons': 14,
'start': '2024-06-20T13:00:00-07:00',
}),
dict({
'gallons': 15,
'start': '2024-06-20T14:00:00-07:00',
}),
dict({
'gallons': 16,
'start': '2024-06-20T15:00:00-07:00',
}),
dict({
'gallons': 17,
'start': '2024-06-20T16:00:00-07:00',
}),
dict({
'gallons': 18,
'start': '2024-06-20T17:00:00-07:00',
}),
dict({
'gallons': 19,
'start': '2024-06-20T18:00:00-07:00',
}),
dict({
'gallons': 20,
'start': '2024-06-20T19:00:00-07:00',
}),
dict({
'gallons': 21,
'start': '2024-06-20T20:00:00-07:00',
}),
dict({
'gallons': 22,
'start': '2024-06-20T21:00:00-07:00',
}),
dict({
'gallons': 23,
'start': '2024-06-20T22:00:00-07:00',
}),
dict({
'gallons': 24,
'start': '2024-06-20T23:00:00-07:00',
}),
]),
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.watersmart_test_gallons_for_most_recent_full_day',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1136',
})
# ---
# name: test_most_recent_hour_sensor
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data scraped from WaterSmart',
'device_class': 'water',
'friendly_name': 'WaterSmart (test) Gallons for most recent hour',
'related': list([
dict({
'gallons': 7.48,
'start': '2024-06-19T19:00:00-07:00',
}),
dict({
'gallons': 0,
'start': '2024-06-19T20:00:00-07:00',
}),
dict({
'gallons': 7.48,
'start': '2024-06-19T21:00:00-07:00',
}),
dict({
'gallons': 14.3,
'start': '2024-06-19T22:00:00-07:00',
}),
]),
'start': '2024-06-19T22:00:00-07:00',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.watersmart_test_gallons_for_most_recent_hour',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '54.1',
})
# ---
# name: test_sensors_for_zero_gallons
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data scraped from WaterSmart',
'device_class': 'water',
'friendly_name': 'WaterSmart (test) Gallons for most recent full day',
'related': list([
]),
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.watersmart_test_gallons_for_most_recent_full_day',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensors_for_zero_gallons.1
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data scraped from WaterSmart',
'device_class': 'water',
'friendly_name': 'WaterSmart (test) Gallons for most recent hour',
'related': list([
dict({
'gallons': 7.48,
'start': '2024-06-19T19:00:00-07:00',
}),
dict({
'gallons': 0,
'start': '2024-06-19T20:00:00-07:00',
}),
dict({
'gallons': 7.48,
'start': '2024-06-19T21:00:00-07:00',
}),
dict({
'gallons': 0,
'start': '2024-06-19T22:00:00-07:00',
}),
]),
'start': '2024-06-19T22:00:00-07:00',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.watersmart_test_gallons_for_most_recent_hour',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
Loading

0 comments on commit 0bf2f6a

Please sign in to comment.