11"""Tests for the NMEA2000 Hub."""
22import pytest
3- from unittest .mock import patch , MagicMock
3+ from unittest .mock import patch , MagicMock , AsyncMock
44
55from pytest_homeassistant_custom_component .common import MockConfigEntry
66from nmea2000 import State
2323from custom_components .nmea2000 .hub import Hub
2424
2525
26+ def _make_gateway_mock ():
27+ """Create a gateway mock with async methods properly mocked."""
28+ mock = MagicMock ()
29+ mock .connect = AsyncMock ()
30+ mock .close = AsyncMock ()
31+ return mock
32+
33+
2634def _make_entry (hass , mode , extra_data = None ):
2735 """Create a MockConfigEntry with the given mode and extra data."""
2836 data = {"name" : "Test" , CONF_MODE : mode }
@@ -36,9 +44,7 @@ def _make_entry(hass, mode, extra_data=None):
3644@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
3745async def test_hub_creates_can_gateway (mock_can_cls , hass ):
3846 """Test Hub creates PythonCanAsyncIOClient for CAN mode."""
39- mock_can_cls .return_value = MagicMock ()
40- mock_can_cls .return_value .set_receive_callback = MagicMock ()
41- mock_can_cls .return_value .set_status_callback = MagicMock ()
47+ mock_can_cls .return_value = _make_gateway_mock ()
4248
4349 entry = _make_entry (hass , CONF_MODE_CAN , {
4450 CONF_CAN_INTERFACE : "socketcan" ,
@@ -57,9 +63,7 @@ async def test_hub_creates_can_gateway(mock_can_cls, hass):
5763@patch ("custom_components.nmea2000.hub.WaveShareNmea2000Gateway" )
5864async def test_hub_creates_usb_gateway (mock_usb_cls , hass ):
5965 """Test Hub creates WaveShareNmea2000Gateway for USB mode."""
60- mock_usb_cls .return_value = MagicMock ()
61- mock_usb_cls .return_value .set_receive_callback = MagicMock ()
62- mock_usb_cls .return_value .set_status_callback = MagicMock ()
66+ mock_usb_cls .return_value = _make_gateway_mock ()
6367
6468 entry = _make_entry (hass , CONF_MODE_USB , {
6569 CONF_SERIAL_PORT : "/dev/ttyUSB0" ,
@@ -74,9 +78,7 @@ async def test_hub_creates_usb_gateway(mock_usb_cls, hass):
7478@patch ("custom_components.nmea2000.hub.EByteNmea2000Gateway" )
7579async def test_hub_creates_tcp_ebyte_gateway (mock_tcp_cls , hass ):
7680 """Test Hub creates EByteNmea2000Gateway for TCP/EBYTE mode."""
77- mock_tcp_cls .return_value = MagicMock ()
78- mock_tcp_cls .return_value .set_receive_callback = MagicMock ()
79- mock_tcp_cls .return_value .set_status_callback = MagicMock ()
81+ mock_tcp_cls .return_value = _make_gateway_mock ()
8082
8183 entry = _make_entry (hass , CONF_MODE_TCP , {
8284 CONF_DEVICE_TYPE : "EBYTE" ,
@@ -100,9 +102,7 @@ async def test_hub_rejects_unknown_mode(hass):
100102@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
101103async def test_hub_can_gateway_receives_pgn_filters (mock_can_cls , hass ):
102104 """Test CAN gateway receives PGN include/exclude lists."""
103- mock_can_cls .return_value = MagicMock ()
104- mock_can_cls .return_value .set_receive_callback = MagicMock ()
105- mock_can_cls .return_value .set_status_callback = MagicMock ()
105+ mock_can_cls .return_value = _make_gateway_mock ()
106106
107107 entry = _make_entry (hass , CONF_MODE_CAN , {
108108 CONF_CAN_INTERFACE : "slcan" ,
@@ -120,7 +120,7 @@ async def test_hub_can_gateway_receives_pgn_filters(mock_can_cls, hass):
120120@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
121121async def test_hub_can_sets_callbacks (mock_can_cls , hass ):
122122 """Test CAN gateway gets receive and status callbacks set."""
123- mock_instance = MagicMock ()
123+ mock_instance = _make_gateway_mock ()
124124 mock_can_cls .return_value = mock_instance
125125
126126 entry = _make_entry (hass , CONF_MODE_CAN , {
@@ -137,9 +137,7 @@ async def test_hub_can_sets_callbacks(mock_can_cls, hass):
137137@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
138138async def test_hub_status_callback_connected (mock_can_cls , hass ):
139139 """Test status callback sets Running state on CONNECTED."""
140- mock_can_cls .return_value = MagicMock ()
141- mock_can_cls .return_value .set_receive_callback = MagicMock ()
142- mock_can_cls .return_value .set_status_callback = MagicMock ()
140+ mock_can_cls .return_value = _make_gateway_mock ()
143141
144142 entry = _make_entry (hass , CONF_MODE_CAN , {
145143 CONF_CAN_INTERFACE : "slcan" ,
@@ -157,9 +155,7 @@ async def test_hub_status_callback_connected(mock_can_cls, hass):
157155@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
158156async def test_hub_status_callback_disconnected (mock_can_cls , hass ):
159157 """Test status callback sets Disconnected state on DISCONNECTED."""
160- mock_can_cls .return_value = MagicMock ()
161- mock_can_cls .return_value .set_receive_callback = MagicMock ()
162- mock_can_cls .return_value .set_status_callback = MagicMock ()
158+ mock_can_cls .return_value = _make_gateway_mock ()
163159
164160 entry = _make_entry (hass , CONF_MODE_CAN , {
165161 CONF_CAN_INTERFACE : "slcan" ,
@@ -176,9 +172,7 @@ async def test_hub_status_callback_disconnected(mock_can_cls, hass):
176172@patch ("custom_components.nmea2000.hub.PythonCanAsyncIOClient" )
177173async def test_hub_hyphenated_name_creates_valid_sensors (mock_can_cls , hass ):
178174 """Test Hub with hyphenated name creates sensors with sanitized unique IDs."""
179- mock_can_cls .return_value = MagicMock ()
180- mock_can_cls .return_value .set_receive_callback = MagicMock ()
181- mock_can_cls .return_value .set_status_callback = MagicMock ()
175+ mock_can_cls .return_value = _make_gateway_mock ()
182176
183177 entry = _make_entry (hass , CONF_MODE_CAN , {
184178 "name" : "YDEN-02" ,
0 commit comments