From f926978f61639a77d9b83d960af758425c77cdfb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:05:34 -1000 Subject: [PATCH 1/8] [core] Make register_component protected, remove runtime checks Instead of runtime null, duplicate, and capacity checks in register_component_, make the method unreachable from outside codegen by removing the public template wrapper and granting friend access to the codegen-generated ::setup() function. Since ESPHOME_COMPONENT_COUNT is set to exactly len(CORE.component_ids) at codegen time, the StaticVector is always correctly sized and the runtime capacity check cannot trigger from codegen. External components that bypassed codegen to call App.register_component() directly will now get a compile error, forcing them to properly declare their components in their config schema. Co-Authored-By: J. Nick Koston --- esphome/core/application.cpp | 19 +------------------ esphome/core/application.h | 11 ++++------- esphome/cpp_helpers.py | 2 +- .../deep_sleep/test_deep_sleep.py | 2 +- .../ota/test_web_server_ota.py | 2 +- tests/dummy_main.cpp | 4 ++-- 6 files changed, 10 insertions(+), 30 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index b1ece86701..f963afa597 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -79,24 +79,7 @@ static void insertion_sort_by_priority(Iterator first, Iterator last) { } } -void Application::register_component_(Component *comp) { - if (comp == nullptr) { - ESP_LOGW(TAG, "Tried to register null component!"); - return; - } - - for (auto *c : this->components_) { - if (comp == c) { - ESP_LOGW(TAG, "Component %s already registered! (%p)", LOG_STR_ARG(c->get_component_log_str()), c); - return; - } - } - if (this->components_.size() >= ESPHOME_COMPONENT_COUNT) { - ESP_LOGE(TAG, "Cannot register component %s - at capacity!", LOG_STR_ARG(comp->get_component_log_str())); - return; - } - this->components_.push_back(comp); -} +void Application::register_component_(Component *comp) { this->components_.push_back(comp); } void Application::setup() { ESP_LOGI(TAG, "Running through setup()"); ESP_LOGV(TAG, "Sorting components by setup priority"); diff --git a/esphome/core/application.h b/esphome/core/application.h index 0cc29af8e7..b3daa4d7f2 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -108,6 +108,9 @@ namespace esphome::socket { class Socket; } // namespace esphome::socket +// Forward declaration for friend access from codegen-generated setup() +void setup(); + namespace esphome { // Teardown timeout constant (in milliseconds) @@ -247,13 +250,6 @@ class Application { /// Reserve space for components to avoid memory fragmentation - /// Register the component in this Application instance. - template C *register_component(C *c) { - static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); - this->register_component_((Component *) c); - return c; - } - /// Set up all the registered components. Call this at the end of your setup() function. void setup(); @@ -508,6 +504,7 @@ class Application { protected: friend Component; friend class socket::Socket; + friend void ::setup(); #ifdef USE_SOCKET_SELECT_SUPPORT /// Fast path for Socket::ready() via friendship - skips negative fd check. diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 954a28d3d1..b673eaa7e1 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -79,7 +79,7 @@ async def register_component(var, config): if name is not None: add(var.set_component_source(LogStringLiteral(name))) - add(App.register_component(var)) + add(App.register_component_(var)) return var diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index 11f1bcb58e..41ddd72feb 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main): main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") assert "deepsleep = new deep_sleep::DeepSleepComponent();" in main_cpp - assert "App.register_component(deepsleep);" in main_cpp + assert "App.register_component_(deepsleep);" in main_cpp def test_deep_sleep_sleep_duration(generate_main): diff --git a/tests/component_tests/ota/test_web_server_ota.py b/tests/component_tests/ota/test_web_server_ota.py index 794eaac9be..4b3a4c705c 100644 --- a/tests/component_tests/ota/test_web_server_ota.py +++ b/tests/component_tests/ota/test_web_server_ota.py @@ -27,7 +27,7 @@ def test_web_server_ota_generated(generate_main: Callable[[str], str]) -> None: assert "global_web_server_base" in main_cpp # Check component is registered - assert "App.register_component(web_server_webserverotacomponent_id)" in main_cpp + assert "App.register_component_(web_server_webserverotacomponent_id)" in main_cpp def test_web_server_ota_with_callbacks(generate_main: Callable[[str], str]) -> None: diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index 52f1fbd319..3ccf35e04d 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -16,10 +16,10 @@ void setup() { auto *log = new logger::Logger(115200); // NOLINT log->pre_setup(); log->set_uart_selection(logger::UART_SELECTION_UART0); - App.register_component(log); + App.register_component_(log); auto *wifi = new wifi::WiFiComponent(); // NOLINT - App.register_component(wifi); + App.register_component_(wifi); wifi::WiFiAP ap; ap.set_ssid("Test SSID"); ap.set_password("password1"); From 73fd1d68d55a7c43c2ca56d24fc97ca2145f65ea Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:07:47 -1000 Subject: [PATCH 2/8] Fix test_cpp_helpers to use register_component_ Co-Authored-By: J. Nick Koston --- tests/unit_tests/test_cpp_helpers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index 2618803fec..82ded409c7 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -16,7 +16,7 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch): async def test_register_component(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component=Mock(return_value=var)) + app_mock = Mock(register_component_=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -29,7 +29,7 @@ async def test_register_component(monkeypatch): assert actual is var assert add_mock.call_count == 2 - app_mock.register_component.assert_called_with(var) + app_mock.register_component_.assert_called_with(var) assert core_mock.component_ids == [] @@ -48,7 +48,7 @@ async def test_register_component__no_component_id(monkeypatch): async def test_register_component__with_setup_priority(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component=Mock(return_value=var)) + app_mock = Mock(register_component_=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -68,5 +68,5 @@ async def test_register_component__with_setup_priority(monkeypatch): assert actual is var add_mock.assert_called() assert add_mock.call_count == 4 - app_mock.register_component.assert_called_with(var) + app_mock.register_component_.assert_called_with(var) assert core_mock.component_ids == [] From e468db0fce6d547d4038ae55c9a64b75d698003a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:08:36 -1000 Subject: [PATCH 3/8] Add friend declaration for original_setup used by cpp unit tests The cpp test framework renames setup() to original_setup() and replaces setup() with the gtest runner, so we need to friend both. Co-Authored-By: J. Nick Koston --- esphome/core/application.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/application.h b/esphome/core/application.h index b3daa4d7f2..194208c701 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -108,8 +108,9 @@ namespace esphome::socket { class Socket; } // namespace esphome::socket -// Forward declaration for friend access from codegen-generated setup() +// Forward declarations for friend access from codegen-generated setup() void setup(); +void original_setup(); // Used by cpp unit tests which replace setup() with gtest runner namespace esphome { @@ -505,6 +506,7 @@ class Application { friend Component; friend class socket::Socket; friend void ::setup(); + friend void ::original_setup(); #ifdef USE_SOCKET_SELECT_SUPPORT /// Fast path for Socket::ready() via friendship - skips negative fd check. From 25ee5c9a281c13570369d8037ef62b7e8d463fef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:16:45 -1000 Subject: [PATCH 4/8] Add NOLINT for redundant-declaration from Arduino.h Arduino.h on ESP8266 already declares void setup(void), so our forward declaration triggers readability-redundant-declaration. Co-Authored-By: J. Nick Koston --- esphome/core/application.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/core/application.h b/esphome/core/application.h index 194208c701..ba3ce13291 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -109,8 +109,8 @@ class Socket; } // namespace esphome::socket // Forward declarations for friend access from codegen-generated setup() -void setup(); -void original_setup(); // Used by cpp unit tests which replace setup() with gtest runner +void setup(); // NOLINT(readability-redundant-declaration) - may be declared in Arduino.h +void original_setup(); // NOLINT(readability-redundant-declaration) - used by cpp unit tests namespace esphome { From 6d1a40f2105078885178a3c604cb95a50435b746 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:27:41 -1000 Subject: [PATCH 5/8] Mark register_component_ as noinline to prevent setup() bloat Without the template wrapper, the compiler inlines the 24-byte function at each of ~92 call sites in setup(), growing setup() by ~284 bytes. Force a function call instead. Co-Authored-By: J. Nick Koston --- esphome/core/application.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/application.h b/esphome/core/application.h index ba3ce13291..ea70f8ac0d 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -521,7 +521,7 @@ class Application { #endif #endif - void register_component_(Component *comp); + __attribute__((noinline)) void register_component_(Component *comp); void calculate_looping_components_(); void add_looping_components_by_state_(bool match_loop_done); From ebde654259119fa74b804d8fcaf6b17506cd06a5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:31:06 -1000 Subject: [PATCH 6/8] Restore template wrapper as protected register_component_ Rename the non-template to register_component_impl_ and add a protected template register_component_ that wraps it. This preserves the compiler optimization behavior (isra clones) while keeping the method inaccessible to external components. Co-Authored-By: J. Nick Koston --- esphome/core/application.cpp | 2 +- esphome/core/application.h | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index f963afa597..9c89a0947b 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -79,7 +79,7 @@ static void insertion_sort_by_priority(Iterator first, Iterator last) { } } -void Application::register_component_(Component *comp) { this->components_.push_back(comp); } +void Application::register_component_impl_(Component *comp) { this->components_.push_back(comp); } void Application::setup() { ESP_LOGI(TAG, "Running through setup()"); ESP_LOGV(TAG, "Sorting components by setup priority"); diff --git a/esphome/core/application.h b/esphome/core/application.h index ea70f8ac0d..8dcfafbb23 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -521,7 +521,14 @@ class Application { #endif #endif - __attribute__((noinline)) void register_component_(Component *comp); + /// Register a component - only callable from codegen-generated setup() via friend access. + template C *register_component_(C *c) { + static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); + this->register_component_impl_((Component *) c); + return c; + } + + void register_component_impl_(Component *comp); void calculate_looping_components_(); void add_looping_components_by_state_(bool match_loop_done); From d2af639e7135e75bf9fda6aa243923cd30d4e13e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:32:27 -1000 Subject: [PATCH 7/8] Keep original template name to preserve compiler optimizations Using the original register_component name (just moved to protected) produces identical compiled output to the baseline, avoiding the symbol name length overhead from renaming. Co-Authored-By: J. Nick Koston --- esphome/core/application.cpp | 2 +- esphome/core/application.h | 6 +++--- esphome/cpp_helpers.py | 2 +- tests/component_tests/deep_sleep/test_deep_sleep.py | 2 +- tests/component_tests/ota/test_web_server_ota.py | 2 +- tests/dummy_main.cpp | 4 ++-- tests/unit_tests/test_cpp_helpers.py | 8 ++++---- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 9c89a0947b..f963afa597 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -79,7 +79,7 @@ static void insertion_sort_by_priority(Iterator first, Iterator last) { } } -void Application::register_component_impl_(Component *comp) { this->components_.push_back(comp); } +void Application::register_component_(Component *comp) { this->components_.push_back(comp); } void Application::setup() { ESP_LOGI(TAG, "Running through setup()"); ESP_LOGV(TAG, "Sorting components by setup priority"); diff --git a/esphome/core/application.h b/esphome/core/application.h index 8dcfafbb23..dba794c496 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -522,13 +522,13 @@ class Application { #endif /// Register a component - only callable from codegen-generated setup() via friend access. - template C *register_component_(C *c) { + template C *register_component(C *c) { static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); - this->register_component_impl_((Component *) c); + this->register_component_((Component *) c); return c; } - void register_component_impl_(Component *comp); + void register_component_(Component *comp); void calculate_looping_components_(); void add_looping_components_by_state_(bool match_loop_done); diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index b673eaa7e1..954a28d3d1 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -79,7 +79,7 @@ async def register_component(var, config): if name is not None: add(var.set_component_source(LogStringLiteral(name))) - add(App.register_component_(var)) + add(App.register_component(var)) return var diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index 41ddd72feb..11f1bcb58e 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main): main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") assert "deepsleep = new deep_sleep::DeepSleepComponent();" in main_cpp - assert "App.register_component_(deepsleep);" in main_cpp + assert "App.register_component(deepsleep);" in main_cpp def test_deep_sleep_sleep_duration(generate_main): diff --git a/tests/component_tests/ota/test_web_server_ota.py b/tests/component_tests/ota/test_web_server_ota.py index 4b3a4c705c..794eaac9be 100644 --- a/tests/component_tests/ota/test_web_server_ota.py +++ b/tests/component_tests/ota/test_web_server_ota.py @@ -27,7 +27,7 @@ def test_web_server_ota_generated(generate_main: Callable[[str], str]) -> None: assert "global_web_server_base" in main_cpp # Check component is registered - assert "App.register_component_(web_server_webserverotacomponent_id)" in main_cpp + assert "App.register_component(web_server_webserverotacomponent_id)" in main_cpp def test_web_server_ota_with_callbacks(generate_main: Callable[[str], str]) -> None: diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index 3ccf35e04d..52f1fbd319 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -16,10 +16,10 @@ void setup() { auto *log = new logger::Logger(115200); // NOLINT log->pre_setup(); log->set_uart_selection(logger::UART_SELECTION_UART0); - App.register_component_(log); + App.register_component(log); auto *wifi = new wifi::WiFiComponent(); // NOLINT - App.register_component_(wifi); + App.register_component(wifi); wifi::WiFiAP ap; ap.set_ssid("Test SSID"); ap.set_password("password1"); diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index 82ded409c7..2618803fec 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -16,7 +16,7 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch): async def test_register_component(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component_=Mock(return_value=var)) + app_mock = Mock(register_component=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -29,7 +29,7 @@ async def test_register_component(monkeypatch): assert actual is var assert add_mock.call_count == 2 - app_mock.register_component_.assert_called_with(var) + app_mock.register_component.assert_called_with(var) assert core_mock.component_ids == [] @@ -48,7 +48,7 @@ async def test_register_component__no_component_id(monkeypatch): async def test_register_component__with_setup_priority(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component_=Mock(return_value=var)) + app_mock = Mock(register_component=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -68,5 +68,5 @@ async def test_register_component__with_setup_priority(monkeypatch): assert actual is var add_mock.assert_called() assert add_mock.call_count == 4 - app_mock.register_component_.assert_called_with(var) + app_mock.register_component.assert_called_with(var) assert core_mock.component_ids == [] From 9967fc54d56e1cee8b1dd5c8b88cbb2a8af9b915 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Feb 2026 21:40:03 -1000 Subject: [PATCH 8/8] Drop template wrapper, call register_component_ directly The template wrapper caused the compiler to make different optimization decisions on setup(), negating the savings. Calling register_component_ directly was -4 bytes. Co-Authored-By: J. Nick Koston --- esphome/core/application.h | 7 ------- esphome/cpp_helpers.py | 2 +- tests/component_tests/deep_sleep/test_deep_sleep.py | 2 +- tests/component_tests/ota/test_web_server_ota.py | 2 +- tests/dummy_main.cpp | 4 ++-- tests/unit_tests/test_cpp_helpers.py | 8 ++++---- 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/esphome/core/application.h b/esphome/core/application.h index dba794c496..ba3ce13291 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -521,13 +521,6 @@ class Application { #endif #endif - /// Register a component - only callable from codegen-generated setup() via friend access. - template C *register_component(C *c) { - static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); - this->register_component_((Component *) c); - return c; - } - void register_component_(Component *comp); void calculate_looping_components_(); diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 954a28d3d1..b673eaa7e1 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -79,7 +79,7 @@ async def register_component(var, config): if name is not None: add(var.set_component_source(LogStringLiteral(name))) - add(App.register_component(var)) + add(App.register_component_(var)) return var diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index 11f1bcb58e..41ddd72feb 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main): main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") assert "deepsleep = new deep_sleep::DeepSleepComponent();" in main_cpp - assert "App.register_component(deepsleep);" in main_cpp + assert "App.register_component_(deepsleep);" in main_cpp def test_deep_sleep_sleep_duration(generate_main): diff --git a/tests/component_tests/ota/test_web_server_ota.py b/tests/component_tests/ota/test_web_server_ota.py index 794eaac9be..4b3a4c705c 100644 --- a/tests/component_tests/ota/test_web_server_ota.py +++ b/tests/component_tests/ota/test_web_server_ota.py @@ -27,7 +27,7 @@ def test_web_server_ota_generated(generate_main: Callable[[str], str]) -> None: assert "global_web_server_base" in main_cpp # Check component is registered - assert "App.register_component(web_server_webserverotacomponent_id)" in main_cpp + assert "App.register_component_(web_server_webserverotacomponent_id)" in main_cpp def test_web_server_ota_with_callbacks(generate_main: Callable[[str], str]) -> None: diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index 52f1fbd319..3ccf35e04d 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -16,10 +16,10 @@ void setup() { auto *log = new logger::Logger(115200); // NOLINT log->pre_setup(); log->set_uart_selection(logger::UART_SELECTION_UART0); - App.register_component(log); + App.register_component_(log); auto *wifi = new wifi::WiFiComponent(); // NOLINT - App.register_component(wifi); + App.register_component_(wifi); wifi::WiFiAP ap; ap.set_ssid("Test SSID"); ap.set_password("password1"); diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index 2618803fec..82ded409c7 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -16,7 +16,7 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch): async def test_register_component(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component=Mock(return_value=var)) + app_mock = Mock(register_component_=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -29,7 +29,7 @@ async def test_register_component(monkeypatch): assert actual is var assert add_mock.call_count == 2 - app_mock.register_component.assert_called_with(var) + app_mock.register_component_.assert_called_with(var) assert core_mock.component_ids == [] @@ -48,7 +48,7 @@ async def test_register_component__no_component_id(monkeypatch): async def test_register_component__with_setup_priority(monkeypatch): var = Mock(base="foo.bar") - app_mock = Mock(register_component=Mock(return_value=var)) + app_mock = Mock(register_component_=Mock(return_value=var)) monkeypatch.setattr(ch, "App", app_mock) core_mock = Mock(component_ids=["foo.bar"]) @@ -68,5 +68,5 @@ async def test_register_component__with_setup_priority(monkeypatch): assert actual is var add_mock.assert_called() assert add_mock.call_count == 4 - app_mock.register_component.assert_called_with(var) + app_mock.register_component_.assert_called_with(var) assert core_mock.component_ids == []