Skip to content

Commit 2b2f407

Browse files
authored
Merge pull request #59 from cloudblue/add_mockers_passthrough
allow mockers passthrough
2 parents 1a4e2c5 + c595364 commit 2b2f407

File tree

5 files changed

+78
-7
lines changed

5 files changed

+78
-7
lines changed

connect/client/testing/fixtures.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ def client_mocker_factory(request):
1111
"""
1212
mocker = None
1313

14-
def _wrapper(base_url='https://example.org/public/v1'):
15-
mocker = ConnectClientMocker(base_url)
14+
def _wrapper(base_url='https://example.org/public/v1', exclude=None):
15+
mocker = ConnectClientMocker(base_url, exclude=exclude)
1616
mocker.start()
1717
return mocker
1818

@@ -32,8 +32,8 @@ def async_client_mocker_factory(request):
3232
"""
3333
mocker = None
3434

35-
def _wrapper(base_url='https://example.org/public/v1'):
36-
mocker = AsyncConnectClientMocker(base_url)
35+
def _wrapper(base_url='https://example.org/public/v1', exclude=None):
36+
mocker = AsyncConnectClientMocker(base_url, exclude=exclude)
3737
mocker.start()
3838
return mocker
3939

connect/client/testing/fluent.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Copyright (c) 2021 Ingram Micro. All Rights Reserved.
55
#
66
import json
7+
import re
78

89
import httpx
910
import responses
@@ -36,8 +37,13 @@ def match(request):
3637

3738

3839
class ConnectClientMocker(_ConnectClientBase):
39-
def __init__(self, base_url):
40+
def __init__(self, base_url, exclude=None):
4041
super().__init__('api_key', endpoint=base_url)
42+
if exclude:
43+
if not isinstance(exclude, (list, tuple, set)):
44+
exclude = [exclude]
45+
for item in exclude:
46+
_mocker.add_passthru(item)
4147

4248
def get(
4349
self,
@@ -169,14 +175,27 @@ def _get_namespace_class(self):
169175

170176

171177
class AsyncConnectClientMocker(ConnectClientMocker):
172-
def __init__(self, base_url):
178+
def __init__(self, base_url, exclude=None):
173179
super().__init__(base_url)
180+
self.exclude = exclude or []
174181

175182
def start(self):
183+
patterns = self.exclude if isinstance(self.exclude, (list, tuple, set)) else [self.exclude]
184+
real_async_transport = httpx.AsyncClient._transport_for_url
185+
186+
def transport_for_url(self, url):
187+
for pattern in patterns:
188+
if (
189+
(isinstance(pattern, re.Pattern) and pattern.match(str(url)))
190+
or (isinstance(pattern, str) and str(url).startswith(pattern))
191+
):
192+
return real_async_transport(self, url)
193+
return _PytestAsyncTransport(_async_mocker)
194+
176195
_monkeypatch.setattr(
177196
httpx.AsyncClient,
178197
'_transport_for_url',
179-
lambda self, url: _PytestAsyncTransport(_async_mocker),
198+
transport_for_url,
180199
)
181200

182201
def reset(self, success=True):

docs/testing.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ async def test_get_all_products():
4848
assert [item async for item in client.products.all()] == expected_response
4949
```
5050

51+
Both the `ConnectClientMocker` and the `AsyncConnectClientMocker` constructors
52+
accept an extra keywork argument `exclude` to exclude urls from mocking.
53+
the exclude argument can be a string, a `re.Pattern` object or a list, tuple or set which elements
54+
can be strings or `re.Pattern`.
55+
5156
Both mockers are also available as pytest fixtures:
5257

5358
``` {.python}
@@ -62,5 +67,8 @@ def test_get_all_products(client_mocker_factory):
6267
assert list(client.products.all()) == expected_response
6368
```
6469

70+
Also the fixtures accept an extra keyword argument `exclude` that is passed
71+
to the underlying mocker constructor.
72+
6573
For more example on how to use the client mocker see the
6674
`tests/client/test_testing.py` file in the github repository.

tests/async_client/test_testing.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import re
2+
3+
import httpx
14
import pytest
25

36
from connect.client import AsyncConnectClient, ClientError
@@ -377,3 +380,23 @@ async def inner_mocking():
377380
mocker.products.all().mock(return_value=[{'id': 'OBJ-0'}])
378381

379382
await inner_mocking()
383+
384+
385+
@pytest.mark.parametrize(
386+
'exclude',
387+
(
388+
'https://www.google.com',
389+
['https://www.google.com', 'https://youtube.com'],
390+
re.compile(r'https://www.google.com(/\w*)?'),
391+
[re.compile(r'https://www.google.com(/\w*)?'), 'https://youtube.com'],
392+
),
393+
)
394+
@pytest.mark.asyncio
395+
async def test_exclude(mocker, exclude):
396+
with AsyncConnectClientMocker('http://localhost', exclude=exclude) as mocker:
397+
mocker.products.create(return_value={'test': 'data'})
398+
client = AsyncConnectClient('api_key', endpoint='http://localhost')
399+
assert await client.products.create(payload={}) == {'test': 'data'}
400+
async with httpx.AsyncClient() as client:
401+
r = await client.get('https://www.google.com')
402+
assert r.status_code == 200

tests/client/test_testing.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
import pytest
24

35
from connect.client import ClientError, ConnectClient
@@ -370,3 +372,22 @@ def inner_mocking():
370372
mocker.products.all().mock(return_value=[{'id': 'OBJ-0'}])
371373

372374
inner_mocking()
375+
376+
377+
@pytest.mark.parametrize(
378+
'exclude',
379+
(
380+
'https://www.google.com',
381+
['https://www.google.com', 'https://youtube.com'],
382+
re.compile(r'https://www.google.com/\w*'),
383+
[re.compile(r'https://www.google.com/\w*'), 'https://youtube.com'],
384+
),
385+
)
386+
def test_exclude(mocker, exclude):
387+
mocked_add_passthru = mocker.patch('connect.client.testing.fluent._mocker.add_passthru')
388+
mocker = ConnectClientMocker('http://localhost', exclude=exclude)
389+
390+
excluded = exclude if isinstance(exclude, list) else [exclude]
391+
392+
for idx, item in enumerate(excluded):
393+
assert mocked_add_passthru.mock_calls[idx][1][0] == item

0 commit comments

Comments
 (0)