Skip to content

Commit 05aa056

Browse files
authored
chore: Update test infrastructure (#1191)
* Updated to modern-ish versions of `pytest` and `pytest-asyncio` * Removed manual async pytestmark * Removed `event_loop` fixture in favor of getting the current running loop, thus allowing `pytest-asyncio` to manage the event loops.
1 parent 49c362b commit 05aa056

22 files changed

Lines changed: 34 additions & 66 deletions

dev_utils/dev_utils/service_helper.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,22 @@ def __init__(
1212
iothub_connection_string,
1313
eventhub_connection_string,
1414
eventhub_consumer_group,
15-
event_loop=None,
1615
executor=None,
1716
):
18-
self._event_loop = event_loop or asyncio.get_event_loop()
1917
self._executor = executor or concurrent.futures.ThreadPoolExecutor()
2018
self._inner_object = ServiceHelperSync(
2119
iothub_connection_string, eventhub_connection_string, eventhub_consumer_group
2220
)
2321

22+
async def _run_in_executor(self, func, *args):
23+
loop = asyncio.get_running_loop()
24+
return await loop.run_in_executor(self._executor, func, *args)
25+
2426
def set_identity(self, device_id, module_id):
2527
return self._inner_object.set_identity(device_id, module_id)
2628

2729
async def set_desired_properties(self, desired_props):
28-
return await self._event_loop.run_in_executor(
29-
self._executor,
30-
self._inner_object.set_desired_properties,
31-
desired_props,
32-
)
30+
return await self._run_in_executor(self._inner_object.set_desired_properties, desired_props)
3331

3432
async def invoke_method(
3533
self,
@@ -38,8 +36,7 @@ async def invoke_method(
3836
connect_timeout_in_seconds=None,
3937
response_timeout_in_seconds=None,
4038
):
41-
return await self._event_loop.run_in_executor(
42-
self._executor,
39+
return await self._run_in_executor(
4340
self._inner_object.invoke_method,
4441
method_name,
4542
payload,
@@ -52,25 +49,21 @@ async def send_c2d(
5249
payload,
5350
properties,
5451
):
55-
return await self._event_loop.run_in_executor(
56-
self._executor, self._inner_object.send_c2d, payload, properties
57-
)
52+
return await self._run_in_executor(self._inner_object.send_c2d, payload, properties)
5853

5954
async def wait_for_eventhub_arrival(self, message_id, timeout=60):
60-
return await self._event_loop.run_in_executor(
61-
self._executor,
55+
return await self._run_in_executor(
6256
self._inner_object.wait_for_eventhub_arrival,
6357
message_id,
6458
timeout,
6559
)
6660

6761
async def get_next_reported_patch_arrival(self, block=True, timeout=240):
68-
return await self._event_loop.run_in_executor(
69-
self._executor,
62+
return await self._run_in_executor(
7063
self._inner_object.get_next_reported_patch_arrival,
7164
block,
7265
timeout,
7366
)
7467

7568
async def shutdown(self):
76-
return await self._event_loop.run_in_executor(self._executor, self._inner_object.shutdown)
69+
return await self._run_in_executor(self._inner_object.shutdown)

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[pytest]
2+
asyncio_mode=auto
23
testdox_format = plaintext
34
addopts = --testdox --timeout 20 --ignore e2e --ignore tests/e2e
45
norecursedirs=__pycache__, *.egg-info

requirements_test.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
pytest < 8.0.0
2-
pytest-asyncio <= 0.16
1+
pytest>=8,<9
2+
pytest-asyncio~=1.2
33
pytest-mock
44
pytest-testdox>=1.1.1
55
pytest-cov

tests/e2e/iothub_e2e/aio/conftest.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# Licensed under the MIT License. See License.txt in the project root for
33
# license information.
44
import pytest
5-
import asyncio
65
from dev_utils import test_env, ServiceHelper
76
import logging
87
import datetime
@@ -56,13 +55,6 @@ def pytest_sessionfinish(session, exitstatus):
5655
print("-----------------------------------")
5756

5857

59-
@pytest.fixture(scope="session")
60-
def event_loop():
61-
loop = asyncio.get_event_loop()
62-
yield loop
63-
loop.close()
64-
65-
6658
@pytest.fixture(scope="function")
6759
async def brand_new_client(device_identity, client_kwargs, service_helper, device_id, module_id):
6860
service_helper.set_identity(device_id, module_id)
@@ -104,12 +96,11 @@ async def client(brand_new_client):
10496

10597

10698
@pytest.fixture(scope="session")
107-
async def service_helper(event_loop, executor):
99+
async def service_helper(executor):
108100
service_helper = ServiceHelper(
109101
iothub_connection_string=test_env.IOTHUB_CONNECTION_STRING,
110102
eventhub_connection_string=test_env.EVENTHUB_CONNECTION_STRING,
111103
eventhub_consumer_group=test_env.EVENTHUB_CONSUMER_GROUP,
112-
event_loop=event_loop,
113104
executor=executor,
114105
)
115106
yield service_helper

tests/e2e/iothub_e2e/aio/test_c2d.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
logger = logging.getLogger(__name__)
1111
logger.setLevel(level=logging.INFO)
1212

13-
pytestmark = pytest.mark.asyncio
1413

1514
# TODO: add tests for various application properties
1615
# TODO: is there a way to call send_c2d so it arrives as an object rather than a JSON string?
@@ -20,8 +19,9 @@
2019
class TestReceiveC2d(object):
2120
@pytest.mark.it("Can receive C2D")
2221
@pytest.mark.quicktest_suite
23-
async def test_receive_c2d(self, client, service_helper, event_loop, leak_tracker):
22+
async def test_receive_c2d(self, client, service_helper, leak_tracker):
2423
leak_tracker.set_initial_object_list()
24+
event_loop = asyncio.get_running_loop()
2525

2626
message = json.dumps(get_random_dict())
2727

tests/e2e/iothub_e2e/aio/test_connect_disconnect.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
logger = logging.getLogger(__name__)
1010
logger.setLevel(level=logging.INFO)
1111

12-
pytestmark = pytest.mark.asyncio
13-
1412

1513
@pytest.mark.describe("Client object")
1614
class TestConnectDisconnect(object):
@@ -44,7 +42,7 @@ async def test_connect_disconnect(self, brand_new_client, leak_tracker):
4442
# see "This assert fails because of initial and secondary disconnects" below
4543
@pytest.mark.skip(reason="two stage disconnect causes assertion in test code")
4644
async def test_connect_in_the_middle_of_disconnect(
47-
self, brand_new_client, event_loop, service_helper, random_message, leak_tracker
45+
self, brand_new_client, service_helper, random_message, leak_tracker
4846
):
4947
"""
5048
Explanation: People will call `connect` inside `on_connection_state_change` handlers.
@@ -54,6 +52,7 @@ async def test_connect_in_the_middle_of_disconnect(
5452
assert client
5553

5654
leak_tracker.set_initial_object_list()
55+
event_loop = asyncio.get_running_loop()
5756

5857
reconnected_event = asyncio.Event()
5958

@@ -112,7 +111,6 @@ async def handle_on_connection_state_change():
112111
async def test_disconnect_in_the_middle_of_connect(
113112
self,
114113
brand_new_client,
115-
event_loop,
116114
service_helper,
117115
random_message,
118116
first_connect,
@@ -128,6 +126,7 @@ async def test_disconnect_in_the_middle_of_connect(
128126
disconnect_on_next_connect_event = False
129127

130128
leak_tracker.set_initial_object_list()
129+
event_loop = asyncio.get_running_loop()
131130

132131
disconnected_event = asyncio.Event()
133132

tests/e2e/iothub_e2e/aio/test_connect_disconnect_stress.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
logger = logging.getLogger(__name__)
1111
logger.setLevel(level=logging.INFO)
1212

13-
pytestmark = pytest.mark.asyncio
14-
1513

1614
@pytest.mark.stress
1715
@pytest.mark.describe("Client object connect/disconnect stress")

tests/e2e/iothub_e2e/aio/test_infrastructure.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import pytest
55
import uuid
66

7-
pytestmark = pytest.mark.asyncio
8-
97

108
@pytest.mark.describe("ServiceHelper object")
119
class TestServiceHelper(object):

tests/e2e/iothub_e2e/aio/test_methods.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
logger = logging.getLogger(__name__)
1212
logger.setLevel(level=logging.INFO)
1313

14-
pytestmark = pytest.mark.asyncio
15-
1614

1715
@pytest.fixture
1816
def method_name():

tests/e2e/iothub_e2e/aio/test_sas_renewal.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
logger = logging.getLogger(__name__)
1212
logger.setLevel(level=logging.INFO)
1313

14-
pytestmark = pytest.mark.asyncio
15-
1614

1715
@pytest.mark.skipif(
1816
test_config.config.auth not in test_config.AUTH_WITH_RENEWING_TOKEN,
@@ -24,10 +22,9 @@ class TestSasRenewal(object):
2422
@pytest.mark.it("Renews and reconnects before expiry")
2523
@pytest.mark.parametrize(*parametrize.connection_retry_disabled_and_enabled)
2624
@pytest.mark.parametrize(*parametrize.auto_connect_disabled_and_enabled)
27-
async def test_sas_renews(
28-
self, client, event_loop, service_helper, random_message, leak_tracker
29-
):
25+
async def test_sas_renews(self, client, service_helper, random_message, leak_tracker):
3026
leak_tracker.set_initial_object_list()
27+
event_loop = asyncio.get_running_loop()
3128

3229
connected_event = asyncio.Event()
3330
disconnected_event = asyncio.Event()

0 commit comments

Comments
 (0)