Skip to content

Commit d856c5e

Browse files
test: fix flaky mqtt adapter tests with events
The `test_standalone/test_mqtt_adapter.py` test suite used `await asyncio.sleep(0.02)` to wait for mocked methods to be called before assertions. Under higher load, or in some configurations like Python 3.13, this constant delay was insufficient, causing intermittent failures such as `AssertionError: Expected 'publish_log' to have been called`. This replaces the arbitrary sleeps with deterministic synchronization primitives (`asyncio.Event`) where tests wait explicitly for up to 1 second for the mocked function to be called before checking assertions. Co-authored-by: rnovatorov <20299819+rnovatorov@users.noreply.github.com>
1 parent 9b6dea0 commit d856c5e

1 file changed

Lines changed: 27 additions & 9 deletions

File tree

tests/unit/test_standalone/test_mqtt_adapter.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ async def test_publish_properties():
5050
device = Device()
5151
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
5252
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
53+
event = asyncio.Event()
54+
device_channel.publish_properties.side_effect = lambda *args, **kwargs: event.set()
5355
mqtt_api_client.device_channel.return_value = device_channel
5456
async with asyncio.TaskGroup() as tg:
5557
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -59,7 +61,7 @@ async def test_publish_properties():
5961
device=device,
6062
task_group=tg,
6163
):
62-
await asyncio.sleep(0.02)
64+
await asyncio.wait_for(event.wait(), timeout=1.0)
6365
device_channel.publish_properties.assert_called()
6466
last_call = device_channel.publish_properties.call_args
6567
published_properties = last_call.kwargs["properties"]
@@ -71,6 +73,8 @@ async def test_publish_telemetry():
7173
device = Device()
7274
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
7375
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
76+
event = asyncio.Event()
77+
device_channel.publish_telemetry.side_effect = lambda *args, **kwargs: event.set()
7478
mqtt_api_client.device_channel.return_value = device_channel
7579
async with asyncio.TaskGroup() as tg:
7680
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -80,7 +84,7 @@ async def test_publish_telemetry():
8084
device=device,
8185
task_group=tg,
8286
):
83-
await asyncio.sleep(0.02)
87+
await asyncio.wait_for(event.wait(), timeout=1.0)
8488
device_channel.publish_telemetry.assert_called()
8589
last_call = device_channel.publish_telemetry.call_args
8690
published_telemetry = last_call.kwargs["telemetry"]
@@ -94,6 +98,8 @@ async def test_publish_logs(log_severity, persist_logs) -> None:
9498
device = Device(log_severity=log_severity, persist_logs=persist_logs)
9599
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
96100
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
101+
event = asyncio.Event()
102+
device_channel.publish_log.side_effect = lambda *args, **kwargs: event.set()
97103
mqtt_api_client.device_channel.return_value = device_channel
98104
async with asyncio.TaskGroup() as tg:
99105
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -103,7 +109,7 @@ async def test_publish_logs(log_severity, persist_logs) -> None:
103109
device=device,
104110
task_group=tg,
105111
):
106-
await asyncio.sleep(0.02)
112+
await asyncio.wait_for(event.wait(), timeout=1.0)
107113
device_channel.publish_log.assert_called()
108114
last_call = device_channel.publish_log.call_args
109115
published_log = last_call.kwargs["log"]
@@ -119,7 +125,11 @@ async def test_publish_properties_exception():
119125
device = Device()
120126
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
121127
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
122-
device_channel.publish_properties.side_effect = RuntimeError("Publish error")
128+
event = asyncio.Event()
129+
def publish_properties_mock(*args, **kwargs):
130+
event.set()
131+
raise RuntimeError("Publish error")
132+
device_channel.publish_properties.side_effect = publish_properties_mock
123133
mqtt_api_client.device_channel.return_value = device_channel
124134
async with asyncio.TaskGroup() as tg:
125135
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -129,15 +139,19 @@ async def test_publish_properties_exception():
129139
device=device,
130140
task_group=tg,
131141
):
132-
await asyncio.sleep(0.02)
142+
await asyncio.wait_for(event.wait(), timeout=1.0)
133143
device_channel.publish_properties.assert_called()
134144

135145

136146
async def test_publish_telemetry_exception():
137147
device = Device()
138148
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
139149
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
140-
device_channel.publish_telemetry.side_effect = RuntimeError("Publish error")
150+
event = asyncio.Event()
151+
def publish_telemetry_mock(*args, **kwargs):
152+
event.set()
153+
raise RuntimeError("Publish error")
154+
device_channel.publish_telemetry.side_effect = publish_telemetry_mock
141155
mqtt_api_client.device_channel.return_value = device_channel
142156
async with asyncio.TaskGroup() as tg:
143157
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -147,15 +161,19 @@ async def test_publish_telemetry_exception():
147161
device=device,
148162
task_group=tg,
149163
):
150-
await asyncio.sleep(0.02)
164+
await asyncio.wait_for(event.wait(), timeout=1.0)
151165
device_channel.publish_telemetry.assert_called()
152166

153167

154168
async def test_publish_logs_exception():
155169
device = Device(log_severity="error")
156170
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
157171
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
158-
device_channel.publish_log.side_effect = RuntimeError("Publish error")
172+
event = asyncio.Event()
173+
def publish_log_mock(*args, **kwargs):
174+
event.set()
175+
raise RuntimeError("Publish error")
176+
device_channel.publish_log.side_effect = publish_log_mock
159177
mqtt_api_client.device_channel.return_value = device_channel
160178
async with asyncio.TaskGroup() as tg:
161179
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
@@ -165,7 +183,7 @@ async def test_publish_logs_exception():
165183
device=device,
166184
task_group=tg,
167185
):
168-
await asyncio.sleep(0.02)
186+
await asyncio.wait_for(event.wait(), timeout=1.0)
169187
device_channel.publish_log.assert_called()
170188

171189

0 commit comments

Comments
 (0)