Skip to content

Commit

Permalink
Enable unit tests in GitHub workflows (#355)
Browse files Browse the repository at this point in the history
  • Loading branch information
weltenwort authored Jan 21, 2024
1 parent d316132 commit a376321
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 64 deletions.
55 changes: 31 additions & 24 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Linting
name: "Linting and Testing"

on:
push:
Expand Down Expand Up @@ -29,7 +29,7 @@ jobs:

- name: Install Python modules
run: |
poetry install
poetry install --no-root
- name: Run pre-commit on all files
run: |
Expand All @@ -56,25 +56,32 @@ jobs:

- name: Hassfest validation
uses: "home-assistant/actions/hassfest@master"
# tests:
# runs-on: "ubuntu-latest"
# name: Run tests
# steps:
# - name: Check out code from GitHub
# uses: "actions/checkout@v4"
# - name: Setup Python
# uses: "actions/setup-python@v5"
# with:
# python-version-file: pyproject.toml
# - name: Install requirements
# run: |
# pip install --constraint=.github/workflows/constraints.txt pip
# pip install -r requirements_test.txt
# - name: Tests suite
# run: |
# pytest \
# --timeout=9 \
# --durations=10 \
# -n auto \
# -p no:sugar \
# tests

test:
runs-on: "ubuntu-latest"
name: "Test"
steps:
- name: Check out the repository
uses: "actions/checkout@v4"

- name: Install poetry
run: pipx install poetry

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: pyproject.toml
cache: "poetry"

- name: Install Python modules
run: |
poetry install --no-root
- name: Run pytest
uses: pavelzw/pytest-action@v2
with:
custom-pytest: "poetry run pytest"
verbose: true
emoji: true
job-summary: true
click-to-expand: false
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Debug Tests",
"type": "python",
"request": "launch",
"program": "${file}",
"purpose": ["debug-test"],
"console": "integratedTerminal",
"justMyCode": false
}
]
}
7 changes: 6 additions & 1 deletion tests/conftest.py → conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

import pytest

pytest_plugins = "pytest_homeassistant_custom_component"
pytest_plugins = ("pytest_homeassistant_custom_component",)


@pytest.fixture(autouse=True)
def auto_enable_custom_integrations(enable_custom_integrations: None):
yield


# This fixture is used to prevent HomeAssistant from attempting to create and dismiss persistent
Expand Down
23 changes: 13 additions & 10 deletions custom_components/rct_power/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@
https://github.com/weltenwort/home-assistant-rct-power-integration
"""
import asyncio
from datetime import timedelta
import logging
from typing import Any, Callable, cast
from datetime import timedelta
from typing import Any
from typing import Callable
from typing import cast
from typing import Literal

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .lib.api import RctPowerApiClient
from .lib.const import (
DOMAIN,
PLATFORMS,
STARTUP_MESSAGE,
)
from .lib.const import DOMAIN
from .lib.const import PLATFORMS
from .lib.const import STARTUP_MESSAGE
from .lib.context import RctPowerContext
from .lib.domain_data import get_domain_data
from .lib.entities import all_entity_descriptions
from .lib.entity import EntityUpdatePriority, resolve_object_infos
from .lib.entry import RctPowerConfigEntryData, RctPowerConfigEntryOptions
from .lib.entity import EntityUpdatePriority
from .lib.entity import resolve_object_infos
from .lib.entry import RctPowerConfigEntryData
from .lib.entry import RctPowerConfigEntryOptions
from .lib.update_coordinator import RctPowerDataUpdateCoordinator

SCAN_INTERVAL = timedelta(seconds=30)
Expand All @@ -37,7 +40,7 @@ async def async_setup(hass: HomeAssistant, config: Config):
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> Literal[True]:
"""Set up this integration using UI."""
if len(domain_data := get_domain_data(hass)) == 0:
_LOGGER.info(STARTUP_MESSAGE)
Expand Down
54 changes: 27 additions & 27 deletions tests/test_init.py → custom_components/rct_power/init_test.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
"""Test RCT Power setup process."""
from dataclasses import asdict

import pytest
from custom_components.rct_power import (
async_reload_entry,
)
from custom_components.rct_power import (
async_setup_entry,
)
from custom_components.rct_power import (
async_unload_entry,
)
from custom_components.rct_power import (
RctPowerDataUpdateCoordinator,
)
from custom_components.rct_power.lib.const import (
DOMAIN,
)
from custom_components.rct_power import async_setup_entry
from custom_components.rct_power import async_unload_entry
from custom_components.rct_power.lib.const import DOMAIN
from custom_components.rct_power.lib.context import RctPowerContext
from custom_components.rct_power.lib.entry import RctPowerConfigEntryData
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from pytest_homeassistant_custom_component.common import MockConfigEntry

Expand All @@ -24,35 +17,42 @@
# 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_unload_and_reload_entry(hass, bypass_get_data):
async def test_setup_unload_and_reload_entry(
hass: HomeAssistant, bypass_get_data: None
):
"""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={}, entry_id="test")
config_entry = MockConfigEntry(
domain=DOMAIN,
data=asdict(RctPowerConfigEntryData(hostname="localhost")),
entry_id="test",
)

config_entry.add_to_hass(hass)
# Set up the entry and assert that the values set during setup are where we expect
# them to be. Because we have patched the RctPowerDataUpdateCoordinator.async_get_data
# call, no code from custom_components/rct_power/api.py actually runs.
assert await async_setup_entry(hass, config_entry)
assert await hass.config_entries.async_setup(config_entry.entry_id)
assert DOMAIN in hass.data and config_entry.entry_id in hass.data[DOMAIN]
assert isinstance(
hass.data[DOMAIN][config_entry.entry_id], RctPowerDataUpdateCoordinator
)
assert isinstance(hass.data[DOMAIN][config_entry.entry_id], RctPowerContext)

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

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


async def test_setup_entry_exception(hass, error_on_get_data):
async def test_setup_entry_exception(hass: HomeAssistant, error_on_get_data: None):
"""Test ConfigEntryNotReady when API raises an exception during entry setup."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, entry_id="test")
config_entry = MockConfigEntry(
domain=DOMAIN,
data=asdict(RctPowerConfigEntryData(hostname="localhost")),
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
Expand Down
30 changes: 29 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ black = "^23.12.1"
flake8 = "^7.0.0"
homeassistant-stubs = "^2024.1.3"
pre-commit = "^3.3.3"
pytest = "^7.4.3"
pytest-emoji = "^0.2.0"
pytest-homeassistant-custom-component = "^0.13.89"
pytest-md = "^0.2.0"
reorder-python-imports = "^3.12.0"

[build-system]
Expand All @@ -25,7 +28,7 @@ build-backend = "poetry.core.masonry.api"

[tool.pyright]
typeCheckingMode = "strict"
reportMissingTypeStubs = "warning"
reportMissingTypeStubs = "none"
reportImportCycles = "warning"
useLibraryCodeForTypes = true

Expand Down

0 comments on commit a376321

Please sign in to comment.