Skip to content

Commit 9deb303

Browse files
committed
Add pytest plugin
Move the betamax fixtures to an installable pytest plugin, so that dependent applications can also make use of them.
1 parent 739c095 commit 9deb303

File tree

7 files changed

+126
-65
lines changed

7 files changed

+126
-65
lines changed

HISTORY.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ Release History
116116
``DefaultNetwork`` as an alias for backwards-compatibility), and added an
117117
optional ``session`` argument, for clients to pass a custom
118118
``requests.Session`` object.
119+
- Added a pytest plugin (which can be installed with ``pip install
120+
boxsdk[pytest]``) that defines ``betamax``-powered fixtures that clients can
121+
use to test their ``boxsdk``-based applications.
119122
- Added ``network_response_constructor`` as an optional property on the
120123
``Network`` interface. Implementations are encouraged to override this
121124
property, and use it to construct ``NetworkResponse`` instances. That way,

boxsdk/pytest_plugin/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# coding: utf-8
2+
3+
from __future__ import absolute_import, unicode_literals
4+
5+
6+
__doc__ = """pytest fixtures that can help with testing boxsdk-powered applications.""" # pylint:disable=redefined-builtin
7+
8+
9+
pytest_plugins = ['boxsdk.pytest_plugin.betamax'] # pylint:disable=invalid-name

boxsdk/pytest_plugin/betamax.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# coding: utf-8
2+
3+
from __future__ import absolute_import, unicode_literals
4+
5+
import os
6+
7+
from betamax import Betamax
8+
import pytest
9+
import requests
10+
11+
from boxsdk import Client
12+
from boxsdk.network.default_network import RequestsSessionNetwork
13+
from boxsdk.session.box_session import BoxSession
14+
15+
16+
# pylint:disable=redefined-outer-name
17+
18+
19+
@pytest.fixture
20+
def real_requests_session():
21+
return requests.Session()
22+
23+
24+
@pytest.fixture(scope='module')
25+
def betamax_cassette_library_dir(request):
26+
"""Each directory test/foo/bar that uses betamax has a directory test/foo/bar/cassettes to hold cassettes."""
27+
return os.path.join(request.fspath.dirname, 'cassettes')
28+
29+
30+
@pytest.fixture
31+
def configure_betamax(betamax_cassette_library_dir):
32+
if not os.path.exists(betamax_cassette_library_dir):
33+
os.makedirs(betamax_cassette_library_dir)
34+
with Betamax.configure() as config:
35+
config.cassette_library_dir = betamax_cassette_library_dir
36+
config.default_cassette_options['re_record_interval'] = 100
37+
config.default_cassette_options['record'] = 'none' if os.environ.get('IS_CI') else 'once'
38+
39+
40+
@pytest.fixture
41+
def betamax_cassette_name(request):
42+
"""The betamax cassette name to use for the test.
43+
44+
The name is the same as the pytest nodeid (e.g.
45+
module_path::parametrized_test_name or
46+
module_path::class_name::parametrized_test_name), but replacing the full
47+
module-path with just the base filename, e.g. test_foo::test_bar[0].
48+
"""
49+
node_ids = request.node.nodeid.split('::')
50+
node_ids[0] = request.fspath.purebasename
51+
return '::'.join(node_ids)
52+
53+
54+
@pytest.fixture(scope='module')
55+
def betamax_use_cassette_kwargs():
56+
return {}
57+
58+
59+
@pytest.fixture
60+
def betamax_recorder(configure_betamax, real_requests_session): # pylint:disable=unused-argument
61+
return Betamax(real_requests_session)
62+
63+
64+
@pytest.fixture
65+
def betamax_cassette_recorder(betamax_recorder, betamax_cassette_name, betamax_use_cassette_kwargs):
66+
"""Including this fixture causes the test to use a betamax cassette for network requests."""
67+
with betamax_recorder.use_cassette(betamax_cassette_name, **betamax_use_cassette_kwargs) as cassette_recorder:
68+
yield cassette_recorder
69+
70+
71+
@pytest.fixture
72+
def betamax_session(betamax_cassette_recorder):
73+
"""A betamax-enabled requests.Session instance."""
74+
return betamax_cassette_recorder.session
75+
76+
77+
@pytest.fixture
78+
def betamax_boxsdk_network(betamax_session):
79+
"""A betamax-enabled boxsdk.Network instance."""
80+
return RequestsSessionNetwork(session=betamax_session)
81+
82+
83+
@pytest.fixture
84+
def betamax_boxsdk_session(betamax_boxsdk_network, betamax_boxsdk_auth):
85+
"""A betamax-enabled boxsdk.BoxSession instance.
86+
87+
Requires an implementation of the abstract `betamax_boxsdk_auth` fixture,
88+
of type `boxsdk.OAuth2`.
89+
"""
90+
return BoxSession(oauth=betamax_boxsdk_auth, network_layer=betamax_boxsdk_network)
91+
92+
93+
@pytest.fixture
94+
def betamax_boxsdk_client(betamax_boxsdk_session, betamax_boxsdk_auth):
95+
"""A betamax-enabled boxsdk.Client instance.
96+
97+
Requires an implementation of the abstract `betamax_boxsdk_auth` fixture,
98+
of type `boxsdk.OAuth2`.
99+
"""
100+
return Client(oauth=betamax_boxsdk_auth, session=betamax_boxsdk_session)

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
-e .[pytest]
12
-r requirements.txt
23
betamax
34
bottle

setup.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def main():
6161
jwt_requires = ['pyjwt>=1.3.0', 'cryptography>=0.9.2']
6262
extra_requires = defaultdict(list)
6363
extra_requires.update({'jwt': jwt_requires, 'redis': redis_requires, 'all': jwt_requires + redis_requires})
64+
extra_requires['pytest'] = ['betamax', 'pytest>=3.0.0']
6465
conditional_dependencies = {
6566
# Newer versions of pip and wheel, which support PEP 426, allow
6667
# environment markers for conditional dependencies to use operators
@@ -97,6 +98,12 @@ def main():
9798
install_requires=install_requires,
9899
extras_require=extra_requires,
99100
tests_require=['betamax', 'pytest', 'pytest-xdist', 'mock', 'sqlalchemy', 'bottle', 'jsonpatch'],
101+
entry_points={
102+
'pytest11': [
103+
# pytest fixtures that can help with testing boxsdk-powered applications.
104+
'boxsdk = boxsdk.pytest_plugin [pytest]',
105+
],
106+
},
100107
cmdclass={'test': PyTest},
101108
classifiers=CLASSIFIERS,
102109
keywords='box oauth2 sdk',

test/conftest.py

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44

55
import json
66
import logging
7-
import os
87
import sys
98

10-
from betamax import Betamax
119
from mock import Mock
1210
import pytest
1311
import requests
1412
from six import binary_type
1513

16-
from boxsdk.network.default_network import DefaultNetworkResponse, RequestsSessionNetwork
14+
from boxsdk.network.default_network import DefaultNetworkResponse
15+
16+
17+
pytest_plugins = ['boxsdk'] # pylint:disable=invalid-name
1718

1819

1920
class RealRequestsSession(requests.Session):
@@ -278,63 +279,3 @@ def mock_user_id():
278279
@pytest.fixture(scope='module')
279280
def mock_group_id():
280281
return 'fake-group-99'
281-
282-
283-
# betamax integration
284-
285-
286-
@pytest.fixture(scope='module')
287-
def betamax_cassette_library_dir(request):
288-
"""Each directory test/foo/bar that uses betamax has a directory test/foo/bar/cassettes to hold cassettes."""
289-
return os.path.join(request.fspath.dirname, 'cassettes')
290-
291-
292-
@pytest.fixture
293-
def configure_betamax(betamax_cassette_library_dir):
294-
with Betamax.configure() as config:
295-
config.cassette_library_dir = betamax_cassette_library_dir
296-
config.default_cassette_options['re_record_interval'] = 100
297-
config.default_cassette_options['record'] = 'none' if os.environ.get('IS_CI') else 'once'
298-
299-
300-
@pytest.fixture
301-
def betamax_cassette_name(request):
302-
"""The betamax cassette name to use for the test.
303-
304-
The name is the same as the pytest nodeid (e.g.
305-
module_path::parametrized_test_name or
306-
module_path::class_name::parametrized_test_name), but replacing the full
307-
module-path with just the base filename, e.g. test_foo::test_bar[0].
308-
"""
309-
node_ids = request.node.nodeid.split('::')
310-
node_ids[0] = request.fspath.purebasename
311-
return '::'.join(node_ids)
312-
313-
314-
@pytest.fixture(scope='module')
315-
def betamax_use_cassette_kwargs():
316-
return {}
317-
318-
319-
@pytest.fixture
320-
def betamax_recorder(configure_betamax, real_requests_session): # pylint:disable=unused-argument
321-
return Betamax(real_requests_session)
322-
323-
324-
@pytest.fixture
325-
def betamax_cassette_recorder(betamax_recorder, betamax_cassette_name, betamax_use_cassette_kwargs):
326-
"""Including this fixture causes the test to use a betamax cassette for network requests."""
327-
with betamax_recorder.use_cassette(betamax_cassette_name, **betamax_use_cassette_kwargs) as cassette_recorder:
328-
yield cassette_recorder
329-
330-
331-
@pytest.fixture
332-
def betamax_session(betamax_cassette_recorder):
333-
"""A betamax-enabled requests.Session instance."""
334-
return betamax_cassette_recorder.session
335-
336-
337-
@pytest.fixture
338-
def betamax_network(betamax_session):
339-
"""A betamax-enabled boxsdk.Network instance."""
340-
return RequestsSessionNetwork(session=betamax_session)

test/unit/network/test_network.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def test_requests_session_network_accepts_custom_session():
8080
mock_requests_session.request.assert_called_once_with('method', 'url')
8181

8282

83-
def test_requests_session_network_works_with_betamax(betamax_network):
84-
response = betamax_network.request(method='GET', url='https://api.box.com/2.0/users/me', access_token='access_token')
83+
def test_requests_session_network_works_with_betamax(betamax_boxsdk_network):
84+
response = betamax_boxsdk_network.request(method='GET', url='https://api.box.com/2.0/users/me', access_token='access_token')
8585
assert response.ok is False
8686
assert response.status_code == 401

0 commit comments

Comments
 (0)