Skip to content

Commit bdfabf5

Browse files
committed
Convert tests by using pytest-aiohttp
1 parent f98e81a commit bdfabf5

File tree

7 files changed

+217
-465
lines changed

7 files changed

+217
-465
lines changed

requirements-dev.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
flake8==3.0.4
1+
flake8==3.1.0
22
coverage==4.2
33
sphinx==1.4.8
44
alabaster>=0.6.2
5-
aiohttp==1.0.3
5+
aiohttp==1.1.5
66
jinja2==2.8
7-
pytest==3.0.3
7+
pytest==3.0.4
88
pytest-cov==2.4.0
9+
yarl==0.7.1
10+
multidict==2.1.2
11+
pytest-aiohttp==0.1.3
912
-e .

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import codecs
2-
from setuptools import setup
32
import os
43
import re
54

5+
from setuptools import setup
66

77
with codecs.open(os.path.join(os.path.abspath(os.path.dirname(
88
__file__)), 'aiohttp_jinja2', '__init__.py'), 'r', 'latin1') as fp:

tests/conftest.py

Lines changed: 0 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -1,229 +0,0 @@
1-
import asyncio
2-
import collections
3-
import gc
4-
import logging
5-
import pytest
6-
import re
7-
import socket
8-
import sys
9-
import warnings
10-
11-
from aiohttp import web
12-
13-
14-
class _AssertWarnsContext:
15-
"""A context manager used to implement TestCase.assertWarns* methods."""
16-
17-
def __init__(self, expected, expected_regex=None):
18-
self.expected = expected
19-
if expected_regex is not None:
20-
expected_regex = re.compile(expected_regex)
21-
self.expected_regex = expected_regex
22-
self.obj_name = None
23-
24-
def __enter__(self):
25-
# The __warningregistry__'s need to be in a pristine state for tests
26-
# to work properly.
27-
for v in sys.modules.values():
28-
if getattr(v, '__warningregistry__', None):
29-
v.__warningregistry__ = {}
30-
self.warnings_manager = warnings.catch_warnings(record=True)
31-
self.warnings = self.warnings_manager.__enter__()
32-
warnings.simplefilter("always", self.expected)
33-
return self
34-
35-
def __exit__(self, exc_type, exc_value, tb):
36-
self.warnings_manager.__exit__(exc_type, exc_value, tb)
37-
if exc_type is not None:
38-
# let unexpected exceptions pass through
39-
return
40-
try:
41-
exc_name = self.expected.__name__
42-
except AttributeError:
43-
exc_name = str(self.expected)
44-
first_matching = None
45-
for m in self.warnings:
46-
w = m.message
47-
if not isinstance(w, self.expected):
48-
continue
49-
if first_matching is None:
50-
first_matching = w
51-
if (self.expected_regex is not None and
52-
not self.expected_regex.search(str(w))):
53-
continue
54-
# store warning for later retrieval
55-
self.warning = w
56-
self.filename = m.filename
57-
self.lineno = m.lineno
58-
return
59-
# Now we simply try to choose a helpful failure message
60-
if first_matching is not None:
61-
__tracebackhide__ = True
62-
assert 0, '"{}" does not match "{}"'.format(
63-
self.expected_regex.pattern, str(first_matching))
64-
if self.obj_name:
65-
__tracebackhide__ = True
66-
assert 0, "{} not triggered by {}".format(exc_name,
67-
self.obj_name)
68-
else:
69-
__tracebackhide__ = True
70-
assert 0, "{} not triggered".format(exc_name)
71-
72-
73-
_LoggingWatcher = collections.namedtuple("_LoggingWatcher",
74-
["records", "output"])
75-
76-
77-
class _CapturingHandler(logging.Handler):
78-
"""
79-
A logging handler capturing all (raw and formatted) logging output.
80-
"""
81-
82-
def __init__(self):
83-
logging.Handler.__init__(self)
84-
self.watcher = _LoggingWatcher([], [])
85-
86-
def flush(self):
87-
pass
88-
89-
def emit(self, record):
90-
self.watcher.records.append(record)
91-
msg = self.format(record)
92-
self.watcher.output.append(msg)
93-
94-
95-
class _AssertLogsContext:
96-
"""A context manager used to implement TestCase.assertLogs()."""
97-
98-
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
99-
100-
def __init__(self, logger_name=None, level=None):
101-
self.logger_name = logger_name
102-
if level:
103-
self.level = logging._nameToLevel.get(level, level)
104-
else:
105-
self.level = logging.INFO
106-
self.msg = None
107-
108-
def __enter__(self):
109-
if isinstance(self.logger_name, logging.Logger):
110-
logger = self.logger = self.logger_name
111-
else:
112-
logger = self.logger = logging.getLogger(self.logger_name)
113-
formatter = logging.Formatter(self.LOGGING_FORMAT)
114-
handler = _CapturingHandler()
115-
handler.setFormatter(formatter)
116-
self.watcher = handler.watcher
117-
self.old_handlers = logger.handlers[:]
118-
self.old_level = logger.level
119-
self.old_propagate = logger.propagate
120-
logger.handlers = [handler]
121-
logger.setLevel(self.level)
122-
logger.propagate = False
123-
return handler.watcher
124-
125-
def __exit__(self, exc_type, exc_value, tb):
126-
self.logger.handlers = self.old_handlers
127-
self.logger.propagate = self.old_propagate
128-
self.logger.setLevel(self.old_level)
129-
if exc_type is not None:
130-
# let unexpected exceptions pass through
131-
return False
132-
if len(self.watcher.records) == 0:
133-
__tracebackhide__ = True
134-
assert 0, ("no logs of level {} or higher triggered on {}"
135-
.format(logging.getLevelName(self.level),
136-
self.logger.name))
137-
138-
139-
@pytest.yield_fixture
140-
def warning():
141-
yield _AssertWarnsContext
142-
143-
144-
@pytest.yield_fixture
145-
def log():
146-
yield _AssertLogsContext
147-
148-
149-
@pytest.fixture
150-
def unused_port():
151-
def f():
152-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
153-
s.bind(('127.0.0.1', 0))
154-
return s.getsockname()[1]
155-
return f
156-
157-
158-
@pytest.yield_fixture
159-
def loop(request):
160-
loop = asyncio.new_event_loop()
161-
asyncio.set_event_loop(None)
162-
163-
yield loop
164-
165-
loop.stop()
166-
loop.run_forever()
167-
loop.close()
168-
gc.collect()
169-
asyncio.set_event_loop(None)
170-
171-
172-
@pytest.yield_fixture
173-
def create_server(loop, unused_port):
174-
app = handler = srv = None
175-
176-
@asyncio.coroutine
177-
def create(*, debug=False, ssl_ctx=None, proto='http', **kwargs):
178-
nonlocal app, handler, srv
179-
app = web.Application(loop=loop, **kwargs)
180-
port = unused_port()
181-
handler = app.make_handler(debug=debug, keep_alive_on=False)
182-
srv = yield from loop.create_server(handler, '127.0.0.1', port,
183-
ssl=ssl_ctx)
184-
if ssl_ctx:
185-
proto += 's'
186-
url = "{}://127.0.0.1:{}".format(proto, port)
187-
return app, url
188-
189-
yield create
190-
191-
@asyncio.coroutine
192-
def finish():
193-
yield from handler.finish_connections()
194-
yield from app.finish()
195-
srv.close()
196-
yield from srv.wait_closed()
197-
198-
loop.run_until_complete(finish())
199-
200-
201-
@pytest.mark.tryfirst
202-
def pytest_pycollect_makeitem(collector, name, obj):
203-
if collector.funcnamefilter(name):
204-
if not callable(obj):
205-
return
206-
item = pytest.Function(name, parent=collector)
207-
if 'run_loop' in item.keywords:
208-
return list(collector._genfunctions(name, obj))
209-
210-
211-
@pytest.mark.tryfirst
212-
def pytest_pyfunc_call(pyfuncitem):
213-
"""
214-
Run asyncio marked test functions in an event loop instead of a normal
215-
function call.
216-
"""
217-
if 'run_loop' in pyfuncitem.keywords:
218-
funcargs = pyfuncitem.funcargs
219-
loop = funcargs['loop']
220-
testargs = {arg: funcargs[arg]
221-
for arg in pyfuncitem._fixtureinfo.argnames}
222-
loop.run_until_complete(pyfuncitem.obj(**testargs))
223-
return True
224-
225-
226-
def pytest_runtest_setup(item):
227-
if 'run_loop' in item.keywords and 'loop' not in item.fixturenames:
228-
# inject an event loop fixture for all async tests
229-
item.fixturenames.append('loop')

tests/test_context_processors.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import aiohttp
2-
import aiohttp_jinja2
31
import asyncio
2+
43
import jinja2
5-
import pytest
4+
from aiohttp import web
5+
6+
import aiohttp_jinja2
67

78

8-
@pytest.mark.run_loop
9-
def test_context_processors(create_server, loop):
9+
@asyncio.coroutine
10+
def test_context_processors(test_client, loop):
1011

1112
@aiohttp_jinja2.template('tmpl.jinja2')
1213
@asyncio.coroutine
1314
def func(request):
1415
return {'bar': 2}
1516

16-
app, url = yield from create_server(
17-
middlewares=[
17+
app = web.Application(loop=loop, middlewares=[
1818
aiohttp_jinja2.context_processors_middleware])
1919
aiohttp_jinja2.setup(app, loader=jinja2.DictLoader(
2020
{'tmpl.jinja2':
@@ -26,41 +26,44 @@ def func(request):
2626
lambda request: {'foo': 1, 'bar': 'should be overwriten'}),
2727
)
2828

29-
app.router.add_route('GET', '/', func)
29+
app.router.add_get('/', func)
30+
31+
client = yield from test_client(app)
3032

31-
resp = yield from aiohttp.request('GET', url, loop=loop)
33+
resp = yield from client.get('/')
3234
assert 200 == resp.status
3335
txt = yield from resp.text()
3436
assert 'foo: 1, bar: 2, path: /' == txt
3537

3638

37-
@pytest.mark.run_loop
38-
def test_context_is_response(create_server, loop):
39+
@asyncio.coroutine
40+
def test_context_is_response(test_client, loop):
3941

4042
@aiohttp_jinja2.template('tmpl.jinja2')
4143
def func(request):
42-
return aiohttp.web_exceptions.HTTPForbidden()
44+
return web.HTTPForbidden()
4345

44-
app, url = yield from create_server()
46+
app = web.Application(loop=loop)
4547
aiohttp_jinja2.setup(app, loader=jinja2.DictLoader(
4648
{'tmpl.jinja2': "template"}))
4749

4850
app.router.add_route('GET', '/', func)
51+
client = yield from test_client(app)
4952

50-
resp = yield from aiohttp.request('GET', url, loop=loop)
53+
resp = yield from client.get('/')
5154
assert 403 == resp.status
5255
yield from resp.release()
5356

5457

55-
@pytest.mark.run_loop
56-
def test_context_processors_new_setup_style(create_server, loop):
58+
@asyncio.coroutine
59+
def test_context_processors_new_setup_style(test_client, loop):
5760

5861
@aiohttp_jinja2.template('tmpl.jinja2')
5962
@asyncio.coroutine
6063
def func(request):
6164
return {'bar': 2}
6265

63-
app, url = yield from create_server()
66+
app = web.Application(loop=loop)
6467
aiohttp_jinja2.setup(
6568
app,
6669
loader=jinja2.DictLoader(
@@ -74,8 +77,9 @@ def func(request):
7477
'bar': 'should be overwriten'})))
7578

7679
app.router.add_route('GET', '/', func)
80+
client = yield from test_client(app)
7781

78-
resp = yield from aiohttp.request('GET', url, loop=loop)
82+
resp = yield from client.get('/')
7983
assert 200 == resp.status
8084
txt = yield from resp.text()
8185
assert 'foo: 1, bar: 2, path: /' == txt

tests/test_jinja_filters.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import aiohttp
2-
import aiohttp_jinja2
31
import asyncio
2+
43
import jinja2
5-
import pytest
4+
from aiohttp import web
5+
6+
import aiohttp_jinja2
67

78

8-
@pytest.mark.run_loop
9-
def test_jinja_filters(create_server, loop):
9+
@asyncio.coroutine
10+
def test_jinja_filters(test_client, loop):
1011

1112
@aiohttp_jinja2.template('tmpl.jinja2')
1213
@asyncio.coroutine
@@ -16,16 +17,17 @@ def index(request):
1617
def add_2(value):
1718
return value + 2
1819

19-
app, url = yield from create_server()
20+
app = web.Application(loop=loop)
2021
aiohttp_jinja2.setup(
2122
app,
2223
loader=jinja2.DictLoader({'tmpl.jinja2': "{{ 5|add_2 }}"}),
2324
filters={'add_2': add_2}
2425
)
2526

2627
app.router.add_route('GET', '/', index)
28+
client = yield from test_client(app)
2729

28-
resp = yield from aiohttp.request('GET', url, loop=loop)
30+
resp = yield from client.get('/')
2931
assert 200 == resp.status
3032
txt = yield from resp.text()
3133
assert '7' == txt

0 commit comments

Comments
 (0)