Full test coverage for the config flow
Reasoning
The config flow is the first interaction a user has with your integration. It is important to ensure that the config flow is working as expected and that the user can set up the integration without any issues or (config flow related) errors.
This means that we want to have 100% test coverage for the config flow. In those tests, we require verification that the flow is able to recover from an error to confirm that the user is able to finish the flow even if something goes wrong. The happy flow tests should also assert the unique ID of the created config entry, so a change in how the unique ID is derived doesn't go unnoticed.
Since we want the user to have a smooth experience using other integration flows, this rule also applies to the reconfigure, reauthentication, and options flows.
The extra added benefit of having tests for an integration is that it introduces the developer to testing, making it easier to write tests for other parts of the integration.
Even though the code used to check the uniqueness of a config entry is most likely touched by the happy flow tests, make sure to also test that the flow doesn't allow adding more than one unique configuration entry to reach complete coverage.
Example implementation
We need to test the following scenarios for each way the config flow can be initiated, either by the user, by discovery, or by an import flow.
The example below shows a basic happy flow initiated by the user.
test_config_flow.py:
async def test_full_flow(
hass: HomeAssistant,
mock_my_client: AsyncMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test full flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "10.0.0.131"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "My integration"
assert result["data"] == {
CONF_HOST: "10.0.0.131",
}
assert result["result"].unique_id == "ABC123"
Every error the flow can show needs a test, for example a connection error, invalid credentials, an invalid field value or an unexpected exception.
After each error, the test fixes the cause and finishes the flow, to show the user can recover from the error.
The flow is finished when it creates the entry, or for the reauthentication and reconfigure flows, when it aborts with reauth_successful or reconfigure_successful.
The example below shows this for the user step.
test_config_flow.py:
@pytest.mark.parametrize(
("exception", "error"),
[
(MyConnectionError, "cannot_connect"),
(MyAuthenticationError, "invalid_auth"),
(Exception, "unknown"),
],
)
async def test_flow_errors(
hass: HomeAssistant,
mock_my_client: AsyncMock,
mock_setup_entry: AsyncMock,
exception: Exception,
error: str,
) -> None:
"""Test we handle errors and recover from them."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
mock_my_client.get_data.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "10.0.0.131"},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
mock_my_client.get_data.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "10.0.0.131"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
Additional resources
More information about config flows can be found in the config flow documentation. More information about testing integrations can be found in the testing documentation.
Exceptions
There are no exceptions to this rule.
Related rules
config-flow: Integration needs to be able to be set up via the UItest-before-configure: Test a connection in the config flowunique-config-entry: Don't allow the same device or service to be able to be set up twicediscovery: Devices can be discoveredreauthentication-flow: Reauthentication needs to be available via the UIreconfiguration-flow: Integrations should have a reconfigure flow