Files
esphome/tests/components
clydebarrowandClaude Opus 5.5 333090f803 [packet_interface] Keep App alive across the bridge tests
The test main constructs App once, and later tests and its listener use
it, so the fixture now replaces App in SetUp and constructs it again in
TearDown, instead of constructing over a live App and leaving it
destroyed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:40:08 +11:00
..
2026-10-10 09:12:40 -10:00
2026-04-06 21:59:05 -10:00
2025-09-26 08:53:21 +12:00
2025-11-23 21:25:24 -06:00

How to write C++ ESPHome unit tests

  1. Locate the folder with your component or create a new one with the same name as the component.
  2. Write the tests. You can add as many .cpp and .h files as you need to organize your tests.

IMPORTANT: wrap all your testing code in a unique namespace to avoid linker collisions when compiling testing binaries that combine many components. By convention, this unique namespace is esphome::component::testing (where "component" is the component under test), for example: esphome::uart::testing.

Platform components

For components that expose to a platform component, create a folder under your component test folder with the platform component name, e.g. binary_sensor and include the relevant .cpp and .h test files there.

Override component code generation for testing

During C++ test builds, to_code is suppressed for every component by default — most components do not need to generate configuration code for a unit test binary.

Manifest overrides

If your component needs to customise code generation behavior for testing — for example to re-enable to_code, supply a lightweight stub, add a test-only dependency, or change any other manifest attribute — create an __init__.py in your component's test directory and define override_manifest:

Top-level component (tests/components/<component>/__init__.py):

from tests.testing_helpers import ComponentManifestOverride


def override_manifest(manifest: ComponentManifestOverride) -> None:
    # Re-enable the component's own to_code (needed when the component must
    # emit C++ setup code that the test binary depends on at link time).
    manifest.enable_codegen()

Or supply a lightweight stub instead of the real to_code:

from tests.testing_helpers import ComponentManifestOverride


def override_manifest(manifest: ComponentManifestOverride) -> None:
    async def to_code_testing(config):
        # Only emit what the C++ tests actually need
        pass

    manifest.to_code = to_code_testing
    manifest.dependencies = manifest.dependencies + ["some_test_only_dep"]

Platform component (tests/components/<component>/<domain>/__init__.py, e.g. tests/components/my_sensor/sensor/__init__.py):

from tests.testing_helpers import ComponentManifestOverride


def override_manifest(manifest: ComponentManifestOverride) -> None:
    manifest.enable_codegen()

override_manifest receives a ComponentManifestOverride that wraps the real manifest. Attribute assignments store an override; reads fall back to the real manifest when no override is present.

Key methods:

Method Effect
manifest.enable_codegen() Remove the to_code suppression, re-enabling code generation; a custom to_code set by another override is kept
manifest.add_resources(*files) Copy extra FileResource files into the build; the component's own file list is still read at build time, after FILTER_SOURCE_FILES sees the defines
manifest.restore() Clear all overrides and added resources, reverting every attribute to the original

The function is called after to_code has already been set to None, so calling enable_codegen() is a deliberate opt-in.

Running component unit tests

(from the repository root)

./script/cpp_unit_test.py component1 component2 ...

The above will compile and run the provided components and their tests.

To run all tests, you can invoke cpp_unit_test.py with the special --all flag:

./script/cpp_unit_test.py --all

To run a specific test suite, you can provide a Google Test filter:

GTEST_FILTER='UART*' ./script/cpp_unit_test.py uart modbus

The process will return 0 for success or nonzero for failure. In case of failure, the errors will be printed out to the console.