Skip to content

Commit

Permalink
Add first test
Browse files Browse the repository at this point in the history
  • Loading branch information
FL550 committed Jun 23, 2023
1 parent 6a0761e commit b960d8c
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 5 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/py-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
name: "Python testing"

on:
push:
branches:
- main
pull_request:

jobs:
tests:
name: "Test package"
runs-on: ubuntu-latest
strategy:
max-parallel: 3
matrix:
python-version: ['3.9', '3.10']
steps:
- name: "Checkout code"
uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: "Cache pip"
uses: actions/cache@v3
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: "Install dependencies"
run: |
python -m pip install --upgrade pip
# Prefer requirements-test.txt
if [ -f requirements-test.txt ]; then
bin/install_requirements requirements-test.txt "${{ secrets.ADMIN_GITHUB_TOKEN }}"
elif [ -f requirements-dev.txt ]; then
bin/install_requirements requirements-dev.txt "${{ secrets.ADMIN_GITHUB_TOKEN }}"
elif [ -f requirements.txt ]; then
bin/install_requirements requirements.txt "${{ secrets.ADMIN_GITHUB_TOKEN }}"
fi
pip install pytest-xdist
if [ -d custom_components ]; then
echo '"""Stub."""' >custom_components/__init__.py
fi
- name: "Run tests with pytest"
if: matrix.python-version != '3.9'
run: |
pytest --basetemp=$RUNNER_TEMP --durations=10 -n auto --dist=loadfile -qq -o console_output_style=count -p no:sugar --asyncio-mode=auto
./bin/check_dirty
18 changes: 13 additions & 5 deletions custom_components/dwd_weather/const.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
"""Constants for the DWD Weather integration."""
from datetime import timedelta
from typing import Final

DOMAIN = "dwd_weather"
INTEGRATION_VERSION = "1.2.28"
MIN_REQUIRED_HA_VERSION = "2022.7.1"
from homeassistant.const import Platform

DEFAULT_NAME = "DWD Weather"
ATTRIBUTION = "Data provided by Deutscher Wetterdienst (DWD)"
DOMAIN: Final = "dwd_weather"
# Platforms
PLATFORMS: Final = [
Platform.SENSOR,
Platform.WEATHER,
]
INTEGRATION_VERSION: Final = "1.2.28"
MIN_REQUIRED_HA_VERSION: Final = "2022.7.1"

DEFAULT_NAME: Final = "DWD Weather"
ATTRIBUTION: Final = "Data provided by Deutscher Wetterdienst (DWD)"
ATTR_LATEST_UPDATE = "latest_update_utc"
ATTR_ISSUE_TIME = "forecast_time_utc"
ATTR_STATION_ID = "station_id"
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for integration."""
75 changes: 75 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# pylint: disable=protected-access,redefined-outer-name
"""Global fixtures for integration."""
# Fixtures allow you to replace functions with a Mock object. You can perform
# many options via the Mock to reflect a particular behavior from the original
# function that you want to see without going through the function's actual logic.
# Fixtures can either be passed into tests as parameters, or if autouse=True, they
# will automatically be used across all tests.
#
# Fixtures that are defined in conftest.py are available across all tests. You can also
# define fixtures within a particular test file to scope them locally.
#
# pytest_homeassistant_custom_component provides some fixtures that are provided by
# Home Assistant core. You can find those fixture definitions here:
# https://github.com/MatthewFlamm/pytest-homeassistant-custom-component/blob/master/pytest_homeassistant_custom_component/common.py
#
# See here for more info: https://docs.pytest.org/en/latest/fixture.html (note that
# pytest includes fixtures OOB which you can use as defined on this page)
from unittest.mock import Mock, patch

import pytest

from custom_components.dwd_weather.connector import DWDWeatherData
from custom_components.dwd_weather.sensor import DWDWeatherForecastSensor

pytest_plugins = "pytest_homeassistant_custom_component" # pylint: disable=invalid-name


# 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):
"""Automatically enable loading custom integrations in all tests."""
yield


# This fixture is used to prevent HomeAssistant from attempting to create and dismiss persistent
# notifications. These calls would fail without this fixture since the persistent_notification
# integration is never loaded during a test.
@pytest.fixture(name="skip_notifications", autouse=True)
def skip_notifications_fixture():
"""Skip notification calls."""
with patch("homeassistant.components.persistent_notification.async_create"), patch(
"homeassistant.components.persistent_notification.async_dismiss"
):
yield


# This fixture, when used, will result in calls to async_get_data to return None. To have the call
# return a value, we would add the `return_value=<VALUE_TO_RETURN>` parameter to the patch call.
@pytest.fixture(name="bypass_get_data")
def bypass_get_data_fixture():
"""Skip calls to get data from API."""
with patch.object(
DWDWeatherData, "async_update", side_effect=Mock()
):
yield
# with patch.object(
# DWDWeatherForecastSensor, "async_get_data", side_effect=Mock()
# ):
# yield


# In this fixture, we are forcing calls to async_get_data to raise an Exception. This is useful
# for exception handling.
@pytest.fixture(name="error_on_get_data")
def error_get_data_fixture():
"""Simulate error when retrieving data from API."""
with patch.object(
DWDWeatherData, "async_update", side_effect=Exception
):
yield
# with patch.object(
# DWDWeatherForecastSensor, "async_get_data", side_effect=Exception
# ):
# yield
20 changes: 20 additions & 0 deletions tests/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Constants for tests."""
from typing import Final
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from custom_components.dwd_weather.const import DEFAULT_WIND_DIRECTION_TYPE, CONF_WEATHER_INTERVAL, CONF_WIND_DIRECTION_TYPE, CONF_STATION_ID

from homeassistant.const import CONF_PASSWORD, CONF_USERNAME

# Mock config data to be used across multiple tests
MOCK_CONFIG: Final = {
CONF_LATITUDE: 0.0,
CONF_LONGITUDE: 0.0,
CONF_NAME: "Test Name",
CONF_WEATHER_INTERVAL: 24,
CONF_WIND_DIRECTION_TYPE: DEFAULT_WIND_DIRECTION_TYPE,
CONF_STATION_ID: "L732",
}

MOCK_USER_INPUT: Final = {
CONF_STATION_ID: "L732",
}
52 changes: 52 additions & 0 deletions tests/test__init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# pylint: disable=protected-access,redefined-outer-name
"""Test integration_blueprint setup process."""

import pytest
from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.dwd_weather import (
async_setup_entry,
async_unload_entry,
DWDWeatherData,
)
from custom_components.dwd_weather.const import DOMAIN, DWDWEATHER_DATA
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import MOCK_CONFIG


# We can pass fixtures as defined in conftest.py to tell pytest to use the fixture
# for a given test. We can also leverage fixtures and mocks that are available in
# Home Assistant using the pytest_homeassistant_custom_component plugin.
# Assertions allow you to verify that the return value of whatever is on the left
# side of the assertion matches with the right side.
async def test_setup_load_and_unload_entry(hass: HomeAssistant, bypass_get_data):
"""Test entry setup and unload."""
# Create a mock entry so we don't have to go through config flow
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")

# Set up the entry and assert that the values set during setup are where we expect
# them to be.
assert await async_setup_entry(hass, config_entry)
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
print(hass.data[DOMAIN][config_entry.entry_id])
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id][DWDWEATHER_DATA], DWDWeatherData
)

# Unload the entry and verify that the data has been removed
assert await async_unload_entry(hass, config_entry)
if DOMAIN in hass.data:
assert config_entry.entry_id not in hass.data[DOMAIN]


# async def test_setup_entry_exception(hass: HomeAssistant, 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")

# # 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)

0 comments on commit b960d8c

Please sign in to comment.