diff --git a/.coveragerc b/.coveragerc
index 2bc76723445..c3ab7f1006f 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -441,7 +441,6 @@ omit =
     homeassistant/components/flick_electric/__init__.py
     homeassistant/components/flick_electric/sensor.py
     homeassistant/components/flock/notify.py
-    homeassistant/components/flume/__init__.py
     homeassistant/components/flume/binary_sensor.py
     homeassistant/components/flume/coordinator.py
     homeassistant/components/flume/entity.py
diff --git a/tests/components/flume/conftest.py b/tests/components/flume/conftest.py
new file mode 100644
index 00000000000..999bbd70ce8
--- /dev/null
+++ b/tests/components/flume/conftest.py
@@ -0,0 +1,167 @@
+"""Flume test fixtures."""
+
+from collections.abc import Generator
+import datetime
+from http import HTTPStatus
+import json
+from unittest.mock import mock_open, patch
+
+import jwt
+import pytest
+import requests
+from requests_mock.mocker import Mocker
+
+from homeassistant.components.flume.const import DOMAIN
+from homeassistant.const import (
+    CONF_CLIENT_ID,
+    CONF_CLIENT_SECRET,
+    CONF_PASSWORD,
+    CONF_USERNAME,
+)
+from homeassistant.core import HomeAssistant
+
+from tests.common import MockConfigEntry
+
+USER_ID = "test-user-id"
+REFRESH_TOKEN = "refresh-token"
+TOKEN_URL = "https://api.flumetech.com/oauth/token"
+DEVICE_LIST_URL = (
+    "https://api.flumetech.com/users/test-user-id/devices?user=true&location=true"
+)
+BRIDGE_DEVICE = {
+    "id": "1234",
+    "type": 1,  # Bridge
+    "location": {
+        "name": "Bridge Location",
+    },
+    "name": "Flume Bridge",
+    "connected": True,
+}
+SENSOR_DEVICE = {
+    "id": "1234",
+    "type": 2,  # Sensor
+    "location": {
+        "name": "Sensor Location",
+    },
+    "name": "Flume Sensor",
+    "connected": True,
+}
+DEVICE_LIST = [BRIDGE_DEVICE, SENSOR_DEVICE]
+NOTIFICATIONS_URL = "https://api.flumetech.com/users/test-user-id/notifications?limit=50&offset=0&sort_direction=ASC"
+NOTIFICATION = {
+    "id": 111111,
+    "device_id": "6248148189204194987",
+    "user_id": USER_ID,
+    "type": 1,
+    "message": "Low Flow Leak triggered at Home. Water has been running for 2 hours averaging 0.43 gallons every minute.",
+    "created_datetime": "2020-01-15T16:33:39.000Z",
+    "title": "Potential Leak Detected!",
+    "read": True,
+    "extra": {
+        "query": {
+            "request_id": "SYSTEM_TRIGGERED_USAGE_ALERT",
+            "since_datetime": "2020-01-15 06:33:59",
+            "until_datetime": "2020-01-15 08:33:59",
+            "tz": "America/Los_Angeles",
+            "bucket": "MIN",
+            "raw": False,
+            "group_multiplier": 2,
+            "device_id": ["6248148189204194987"],
+        }
+    },
+    "event_rule": "Low Flow Leak",
+}
+
+NOTIFICATIONS_LIST = [NOTIFICATION]
+
+
+@pytest.fixture(name="config_entry")
+def config_entry_fixture(hass: HomeAssistant) -> MockConfigEntry:
+    """Fixture to create a config entry."""
+    config_entry = MockConfigEntry(
+        domain=DOMAIN,
+        title="test-username",
+        unique_id="test-username",
+        data={
+            CONF_USERNAME: "test-username",
+            CONF_PASSWORD: "test-password",
+            CONF_CLIENT_ID: "client_id",
+            CONF_CLIENT_SECRET: "client_secret",
+        },
+    )
+    config_entry.add_to_hass(hass)
+    return config_entry
+
+
+def encode_access_token() -> str:
+    """Encode the payload of the access token."""
+    expiration_time = datetime.datetime.now() + datetime.timedelta(hours=12)
+    payload = {
+        "user_id": USER_ID,
+        "exp": int(expiration_time.timestamp()),
+    }
+    return jwt.encode(payload, key="secret")
+
+
+@pytest.fixture(name="access_token")
+def access_token_fixture(requests_mock: Mocker) -> Generator[None, None, None]:
+    """Fixture to setup the access token."""
+    token_response = {
+        "refresh_token": REFRESH_TOKEN,
+        "access_token": encode_access_token(),
+    }
+    requests_mock.register_uri(
+        "POST",
+        TOKEN_URL,
+        status_code=HTTPStatus.OK,
+        json={"data": [token_response]},
+    )
+    with patch("builtins.open", mock_open(read_data=json.dumps(token_response))):
+        yield
+
+
+@pytest.fixture(name="device_list")
+def device_list_fixture(requests_mock: Mocker) -> None:
+    """Fixture to setup the device list API response access token."""
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        status_code=HTTPStatus.OK,
+        json={
+            "data": DEVICE_LIST,
+        },
+    )
+
+
+@pytest.fixture(name="device_list_timeout")
+def device_list_timeout_fixture(requests_mock: Mocker) -> None:
+    """Fixture to test a timeout when connecting to the device list url."""
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        exc=requests.exceptions.ConnectTimeout,
+    )
+
+
+@pytest.fixture(name="device_list_unauthorized")
+def device_list_unauthorized_fixture(requests_mock: Mocker) -> None:
+    """Fixture to test an authorized error from the device list url."""
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        status_code=HTTPStatus.UNAUTHORIZED,
+        json={},
+    )
+
+
+@pytest.fixture(name="notifications_list")
+def notifications_list_fixture(requests_mock: Mocker) -> None:
+    """Fixture to setup the device list API response access token."""
+    requests_mock.register_uri(
+        "GET",
+        NOTIFICATIONS_URL,
+        status_code=HTTPStatus.OK,
+        json={
+            "data": NOTIFICATIONS_LIST,
+        },
+    )
diff --git a/tests/components/flume/test_config_flow.py b/tests/components/flume/test_config_flow.py
index 706cee44739..915299223e9 100644
--- a/tests/components/flume/test_config_flow.py
+++ b/tests/components/flume/test_config_flow.py
@@ -1,8 +1,11 @@
 """Test the flume config flow."""
 
-from unittest.mock import MagicMock, patch
+from http import HTTPStatus
+from unittest.mock import patch
 
+import pytest
 import requests.exceptions
+from requests_mock.mocker import Mocker
 
 from homeassistant import config_entries
 from homeassistant.components.flume.const import DOMAIN
@@ -15,15 +18,12 @@ from homeassistant.const import (
 from homeassistant.core import HomeAssistant
 from homeassistant.data_entry_flow import FlowResultType
 
+from .conftest import DEVICE_LIST, DEVICE_LIST_URL
+
 from tests.common import MockConfigEntry
 
 
-def _get_mocked_flume_device_list():
-    flume_device_list_mock = MagicMock()
-    type(flume_device_list_mock).device_list = ["mock"]
-    return flume_device_list_mock
-
-
+@pytest.mark.usefixtures("access_token", "device_list")
 async def test_form(hass: HomeAssistant) -> None:
     """Test we get the form and can setup from user input."""
 
@@ -33,17 +33,7 @@ async def test_form(hass: HomeAssistant) -> None:
     assert result["type"] is FlowResultType.FORM
     assert result["errors"] == {}
 
-    mock_flume_device_list = _get_mocked_flume_device_list()
-
     with (
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
-            return_value=True,
-        ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            return_value=mock_flume_device_list,
-        ),
         patch(
             "homeassistant.components.flume.async_setup_entry",
             return_value=True,
@@ -71,66 +61,57 @@ async def test_form(hass: HomeAssistant) -> None:
     assert len(mock_setup_entry.mock_calls) == 1
 
 
-async def test_form_invalid_auth(hass: HomeAssistant) -> None:
+@pytest.mark.usefixtures("access_token")
+async def test_form_invalid_auth(hass: HomeAssistant, requests_mock: Mocker) -> None:
     """Test we handle invalid auth."""
     result = await hass.config_entries.flow.async_init(
         DOMAIN, context={"source": config_entries.SOURCE_USER}
     )
 
-    with (
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
-            return_value=True,
-        ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            side_effect=Exception,
-        ),
-    ):
-        result2 = await hass.config_entries.flow.async_configure(
-            result["flow_id"],
-            {
-                CONF_USERNAME: "test-username",
-                CONF_PASSWORD: "test-password",
-                CONF_CLIENT_ID: "client_id",
-                CONF_CLIENT_SECRET: "client_secret",
-            },
-        )
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        status_code=HTTPStatus.UNAUTHORIZED,
+        json={"message": "Failure"},
+    )
+
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        {
+            CONF_USERNAME: "test-username",
+            CONF_PASSWORD: "test-password",
+            CONF_CLIENT_ID: "client_id",
+            CONF_CLIENT_SECRET: "client_secret",
+        },
+    )
 
     assert result2["type"] is FlowResultType.FORM
     assert result2["errors"] == {"password": "invalid_auth"}
 
 
+@pytest.mark.usefixtures("access_token", "device_list_timeout")
 async def test_form_cannot_connect(hass: HomeAssistant) -> None:
     """Test we handle cannot connect error."""
     result = await hass.config_entries.flow.async_init(
         DOMAIN, context={"source": config_entries.SOURCE_USER}
     )
-    with (
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
-            return_value=True,
-        ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            side_effect=requests.exceptions.ConnectionError(),
-        ),
-    ):
-        result2 = await hass.config_entries.flow.async_configure(
-            result["flow_id"],
-            {
-                CONF_USERNAME: "test-username",
-                CONF_PASSWORD: "test-password",
-                CONF_CLIENT_ID: "client_id",
-                CONF_CLIENT_SECRET: "client_secret",
-            },
-        )
+
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        {
+            CONF_USERNAME: "test-username",
+            CONF_PASSWORD: "test-password",
+            CONF_CLIENT_ID: "client_id",
+            CONF_CLIENT_SECRET: "client_secret",
+        },
+    )
 
     assert result2["type"] is FlowResultType.FORM
     assert result2["errors"] == {"base": "cannot_connect"}
 
 
-async def test_reauth(hass: HomeAssistant) -> None:
+@pytest.mark.usefixtures("access_token")
+async def test_reauth(hass: HomeAssistant, requests_mock: Mocker) -> None:
     """Test we can reauth."""
     entry = MockConfigEntry(
         domain=DOMAIN,
@@ -151,35 +132,28 @@ async def test_reauth(hass: HomeAssistant) -> None:
     assert result["type"] is FlowResultType.FORM
     assert result["step_id"] == "reauth_confirm"
 
-    with (
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
-            return_value=True,
-        ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            side_effect=Exception,
-        ),
-    ):
-        result2 = await hass.config_entries.flow.async_configure(
-            result["flow_id"],
-            {
-                CONF_PASSWORD: "test-password",
-            },
-        )
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        {
+            CONF_PASSWORD: "test-password",
+        },
+    )
 
     assert result2["type"] is FlowResultType.FORM
     assert result2["errors"] == {"password": "invalid_auth"}
 
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        exc=requests.exceptions.ConnectTimeout,
+    )
+
     with (
         patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
+            "homeassistant.components.flume.config_flow.os.path.exists",
             return_value=True,
         ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            side_effect=requests.exceptions.ConnectionError(),
-        ),
+        patch("homeassistant.components.flume.config_flow.os.unlink") as mock_unlink,
     ):
         result3 = await hass.config_entries.flow.async_configure(
             result2["flow_id"],
@@ -187,21 +161,22 @@ async def test_reauth(hass: HomeAssistant) -> None:
                 CONF_PASSWORD: "test-password",
             },
         )
+        # The existing token file was removed
+        assert len(mock_unlink.mock_calls) == 1
 
     assert result3["type"] is FlowResultType.FORM
     assert result3["errors"] == {"base": "cannot_connect"}
 
-    mock_flume_device_list = _get_mocked_flume_device_list()
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        status_code=HTTPStatus.OK,
+        json={
+            "data": DEVICE_LIST,
+        },
+    )
 
     with (
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeAuth",
-            return_value=True,
-        ),
-        patch(
-            "homeassistant.components.flume.config_flow.FlumeDeviceList",
-            return_value=mock_flume_device_list,
-        ),
         patch(
             "homeassistant.components.flume.async_setup_entry",
             return_value=True,
@@ -217,3 +192,31 @@ async def test_reauth(hass: HomeAssistant) -> None:
     assert mock_setup_entry.called
     assert result4["type"] is FlowResultType.ABORT
     assert result4["reason"] == "reauth_successful"
+
+
+@pytest.mark.usefixtures("access_token")
+async def test_form_no_devices(hass: HomeAssistant, requests_mock: Mocker) -> None:
+    """Test a device list response that contains no values will raise an error."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    requests_mock.register_uri(
+        "GET",
+        DEVICE_LIST_URL,
+        status_code=HTTPStatus.OK,
+        json={"data": []},
+    )
+
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        {
+            CONF_USERNAME: "test-username",
+            CONF_PASSWORD: "test-password",
+            CONF_CLIENT_ID: "client_id",
+            CONF_CLIENT_SECRET: "client_secret",
+        },
+    )
+
+    assert result2["type"] is FlowResultType.FORM
+    assert result2["errors"] == {"base": "cannot_connect"}
diff --git a/tests/components/flume/test_init.py b/tests/components/flume/test_init.py
new file mode 100644
index 00000000000..44a66425949
--- /dev/null
+++ b/tests/components/flume/test_init.py
@@ -0,0 +1,135 @@
+"""Test the flume init."""
+
+from collections.abc import Generator
+from unittest.mock import patch
+
+import pytest
+from requests_mock.mocker import Mocker
+
+from homeassistant import config_entries
+from homeassistant.components.flume.const import DOMAIN
+from homeassistant.const import Platform
+from homeassistant.core import HomeAssistant
+
+from .conftest import USER_ID
+
+from tests.common import MockConfigEntry
+
+
+@pytest.fixture(autouse=True)
+def platforms_fixture() -> Generator[list[str]]:
+    """Return the platforms to be loaded for this test."""
+    # Arbitrary platform to ensure notifications are loaded
+    with patch("homeassistant.components.flume.PLATFORMS", [Platform.BINARY_SENSOR]):
+        yield
+
+
+@pytest.mark.usefixtures("access_token", "device_list")
+async def test_setup_config_entry(
+    hass: HomeAssistant,
+    requests_mock: Mocker,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test load and unload of a ConfigEntry."""
+    assert await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert config_entry.state is config_entries.ConfigEntryState.LOADED
+
+    assert await hass.config_entries.async_unload(config_entry.entry_id)
+    assert config_entry.state is config_entries.ConfigEntryState.NOT_LOADED
+
+
+@pytest.mark.usefixtures("access_token", "device_list_timeout")
+async def test_device_list_timeout(
+    hass: HomeAssistant,
+    requests_mock: Mocker,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test error handling for a timeout when listing devices."""
+    assert not await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert config_entry.state is config_entries.ConfigEntryState.SETUP_RETRY
+
+
+@pytest.mark.usefixtures("access_token", "device_list_unauthorized")
+async def test_reauth_when_unauthorized(
+    hass: HomeAssistant,
+    requests_mock: Mocker,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test error handling for an authentication error when listing devices."""
+    assert not await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert config_entry.state is config_entries.ConfigEntryState.SETUP_ERROR
+
+    flows = hass.config_entries.flow.async_progress()
+    assert len(flows) == 1
+    assert flows[0]["step_id"] == "reauth_confirm"
+
+
+@pytest.mark.usefixtures("access_token", "device_list", "notifications_list")
+async def test_list_notifications_service(
+    hass: HomeAssistant,
+    requests_mock: Mocker,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test the list notifications service."""
+    assert await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+    assert config_entry.state is config_entries.ConfigEntryState.LOADED
+
+    response = await hass.services.async_call(
+        DOMAIN,
+        "list_notifications",
+        {},
+        target={
+            "config_entry": config_entry.entry_id,
+        },
+        blocking=True,
+        return_response=True,
+    )
+    notifications = response.get("notifications")
+    assert notifications
+    assert len(notifications) == 1
+    assert notifications[0].get("user_id") == USER_ID
+
+
+@pytest.mark.usefixtures("access_token", "device_list", "notifications_list")
+async def test_list_notifications_service_config_entry_errors(
+    hass: HomeAssistant,
+    requests_mock: Mocker,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test error handling for notification service with invalid config entries."""
+    assert await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+    assert config_entry.state is config_entries.ConfigEntryState.LOADED
+    assert await hass.config_entries.async_unload(config_entry.entry_id)
+    assert config_entry.state is config_entries.ConfigEntryState.NOT_LOADED
+
+    with pytest.raises(ValueError, match="Config entry not loaded"):
+        await hass.services.async_call(
+            DOMAIN,
+            "list_notifications",
+            {},
+            target={
+                "config_entry": config_entry.entry_id,
+            },
+            blocking=True,
+            return_response=True,
+        )
+
+    with pytest.raises(ValueError, match="Invalid config entry: does-not-exist"):
+        await hass.services.async_call(
+            DOMAIN,
+            "list_notifications",
+            {},
+            target={
+                "config_entry": "does-not-exist",
+            },
+            blocking=True,
+            return_response=True,
+        )