feat(testing): Add free functions for simulating WSGI requests (#859)
Add free simulate_request, etc. functions that can be used without requiring a unittest-style OO/inheritance model. Most notably, this makes it easier to use pytest's more functional style when testing falcon-based apps. As part of this work, refactor the existing WSGI simulation logic to layer on top of the free functions. Don't break backwards compatibility. Partially implements #16
This commit is contained in:
committed by
Fran Fitzpatrick
parent
65a4203422
commit
498b4b688e
@@ -3,20 +3,11 @@
|
||||
Testing
|
||||
=======
|
||||
|
||||
.. autoclass:: falcon.testing.TestCase
|
||||
:members:
|
||||
|
||||
.. autoclass:: falcon.testing.Result
|
||||
:members:
|
||||
|
||||
.. autoclass:: falcon.testing.SimpleTestResource
|
||||
:members:
|
||||
|
||||
.. autoclass:: falcon.testing.StartResponseMock
|
||||
:members:
|
||||
|
||||
.. automodule:: falcon.testing
|
||||
:members: capture_responder_args, rand_string, create_environ
|
||||
:members: Result, TestCase, SimpleTestResource, StartResponseMock,
|
||||
simulate_request, simulate_get, simulate_head, simulate_post,
|
||||
simulate_put, simulate_options, simulate_patch, simulate_delete,
|
||||
capture_responder_args, rand_string, create_environ
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
@@ -37,8 +37,9 @@ For additional examples, see also Falcon's own test suite.
|
||||
|
||||
# Hoist classes and functions into the falcon.testing namespace
|
||||
from falcon.testing.base import TestBase # NOQA
|
||||
from falcon.testing.client import * # NOQA
|
||||
from falcon.testing.helpers import * # NOQA
|
||||
from falcon.testing.resource import capture_responder_args # NOQA
|
||||
from falcon.testing.resource import SimpleTestResource, TestResource # NOQA
|
||||
from falcon.testing.srmock import StartResponseMock # NOQA
|
||||
from falcon.testing.test_case import Result, TestCase # NOQA
|
||||
from falcon.testing.test_case import TestCase # NOQA
|
||||
|
||||
321
falcon/testing/client.py
Normal file
321
falcon/testing/client.py
Normal file
@@ -0,0 +1,321 @@
|
||||
# Copyright 2016 by Rackspace Hosting, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Copyright 2016 by Rackspace Hosting, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""WSGI test client utilities.
|
||||
|
||||
This package includes utilities for simulating HTTP requests against a
|
||||
WSGI callable, without having to stand up a WSGI server.
|
||||
"""
|
||||
|
||||
import json
|
||||
import wsgiref.validate
|
||||
|
||||
from falcon.testing import helpers
|
||||
from falcon.testing.srmock import StartResponseMock
|
||||
from falcon.util import CaseInsensitiveDict
|
||||
|
||||
|
||||
class Result(object):
|
||||
"""Encapsulates the result of a simulated WSGI request.
|
||||
|
||||
Args:
|
||||
iterable (iterable): An iterable that yields zero or more
|
||||
bytestrings, per PEP-3333
|
||||
status (str): An HTTP status string, including status code and
|
||||
reason string
|
||||
headers (list): A list of (header_name, header_value) tuples,
|
||||
per PEP-3333
|
||||
|
||||
Attributes:
|
||||
status (str): HTTP status string given in the response
|
||||
status_code (int): The code portion of the HTTP status string
|
||||
headers (CaseInsensitiveDict): A case-insensitive dictionary
|
||||
containing all the headers in the response
|
||||
encoding (str): Text encoding of the response body, or ``None``
|
||||
if the encoding can not be determined.
|
||||
content (bytes): Raw response body, or ``bytes`` if the
|
||||
response body was empty.
|
||||
text (str): Decoded response body of type ``unicode``
|
||||
under Python 2.6 and 2.7, and of type ``str`` otherwise.
|
||||
If the content type does not specify an encoding, UTF-8 is
|
||||
assumed.
|
||||
json (dict): Deserialized JSON body. Raises an error if the
|
||||
response is not JSON.
|
||||
"""
|
||||
|
||||
def __init__(self, iterable, status, headers):
|
||||
self._text = None
|
||||
|
||||
self._content = b''.join(iterable)
|
||||
if hasattr(iterable, 'close'):
|
||||
iterable.close()
|
||||
|
||||
self._status = status
|
||||
self._status_code = int(status[:3])
|
||||
self._headers = CaseInsensitiveDict(headers)
|
||||
|
||||
self._encoding = helpers.get_encoding_from_headers(self._headers)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
return self._status_code
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
return self._headers
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
return self._encoding
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
return self._content
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
if self._text is None:
|
||||
if not self.content:
|
||||
self._text = u''
|
||||
else:
|
||||
if self.encoding is None:
|
||||
encoding = 'UTF-8'
|
||||
else:
|
||||
encoding = self.encoding
|
||||
|
||||
self._text = self.content.decode(encoding)
|
||||
|
||||
return self._text
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
return json.loads(self.text)
|
||||
|
||||
|
||||
def simulate_request(app, method='GET', path='/', query_string=None,
|
||||
headers=None, body=None, file_wrapper=None):
|
||||
"""Simulates a request to a WSGI application.
|
||||
|
||||
Performs a request against a WSGI application callable.
|
||||
|
||||
Keyword Args:
|
||||
app (callable): The WSGI application to call
|
||||
method (str): An HTTP method to use in the request
|
||||
(default: 'GET')
|
||||
path (str): The URL path to request (default: '/')
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
body (str): A string to send as the body of the request.
|
||||
Accepts both byte strings and Unicode strings
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
file_wrapper (callable): Callable that returns an iterable,
|
||||
to be used as the value for *wsgi.file_wrapper* in the
|
||||
environ (default: ``None``).
|
||||
|
||||
Returns:
|
||||
:py:class:`~.Result`: The result of the request
|
||||
"""
|
||||
|
||||
if not path.startswith('/'):
|
||||
raise ValueError("path must start with '/'")
|
||||
|
||||
if query_string and query_string.startswith('?'):
|
||||
raise ValueError("query_string should not start with '?'")
|
||||
|
||||
if '?' in path:
|
||||
# NOTE(kgriffs): We could allow this, but then we'd need
|
||||
# to define semantics regarding whether the path takes
|
||||
# precedence over the query_string. Also, it would make
|
||||
# tests less consistent, since there would be "more than
|
||||
# one...way to do it."
|
||||
raise ValueError(
|
||||
'path may not contain a query string. Please use the '
|
||||
'query_string parameter instead.'
|
||||
)
|
||||
|
||||
env = helpers.create_environ(
|
||||
method=method,
|
||||
path=path,
|
||||
query_string=(query_string or ''),
|
||||
headers=headers,
|
||||
body=body,
|
||||
file_wrapper=file_wrapper,
|
||||
)
|
||||
|
||||
srmock = StartResponseMock()
|
||||
validator = wsgiref.validate.validator(app)
|
||||
iterable = validator(env, srmock)
|
||||
|
||||
result = Result(iterable, srmock.status, srmock.headers)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def simulate_get(app, path, **kwargs):
|
||||
"""Simulates a GET request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'GET', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return simulate_request(app, 'GET', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_head(app, path, **kwargs):
|
||||
"""Simulates a HEAD request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'HEAD', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return simulate_request(app, 'HEAD', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_post(app, path, **kwargs):
|
||||
"""Simulates a POST request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'POST', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
body (str): A string to send as the body of the request.
|
||||
Accepts both byte strings and Unicode strings
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return simulate_request(app, 'POST', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_put(app, path, **kwargs):
|
||||
"""Simulates a PUT request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'PUT', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
body (str): A string to send as the body of the request.
|
||||
Accepts both byte strings and Unicode strings
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return simulate_request(app, 'PUT', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_options(app, path, **kwargs):
|
||||
"""Simulates an OPTIONS request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'OPTIONS', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return simulate_request(app, 'OPTIONS', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_patch(app, path, **kwargs):
|
||||
"""Simulates a PATCH request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'PATCH', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
body (str): A string to send as the body of the request.
|
||||
Accepts both byte strings and Unicode strings
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return simulate_request(app, 'PATCH', path, **kwargs)
|
||||
|
||||
|
||||
def simulate_delete(app, path, **kwargs):
|
||||
"""Simulates a DELETE request to a WSGI application.
|
||||
|
||||
Equivalent to ``simulate_request(app, 'DELETE', ...)``
|
||||
|
||||
Args:
|
||||
app (callable): The WSGI application to call
|
||||
path (str): The URL path to request
|
||||
|
||||
Keyword Args:
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return simulate_request(app, 'DELETE', path, **kwargs)
|
||||
@@ -32,6 +32,7 @@ import six
|
||||
|
||||
from falcon.util import http_now, uri
|
||||
|
||||
|
||||
# Constants
|
||||
DEFAULT_HOST = 'falconframework.org'
|
||||
|
||||
@@ -185,6 +186,11 @@ def create_environ(path='/', query_string='', protocol='HTTP/1.1',
|
||||
return env
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Private
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
|
||||
def _add_headers_to_environ(env, headers):
|
||||
if not isinstance(headers, dict):
|
||||
# Try to convert
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2013 by Rackspace Hosting, Inc.
|
||||
# Copyright 2016 by Rackspace Hosting, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,6 @@ This package includes a unittest-style base class and requests-like
|
||||
utilities for simulating and validating HTTP requests.
|
||||
"""
|
||||
|
||||
import json
|
||||
import wsgiref.validate
|
||||
|
||||
try:
|
||||
import testtools as unittest
|
||||
except ImportError: # pragma: nocover
|
||||
@@ -28,90 +25,8 @@ except ImportError: # pragma: nocover
|
||||
|
||||
import falcon
|
||||
import falcon.request
|
||||
from falcon.testing.helpers import create_environ, get_encoding_from_headers
|
||||
from falcon.testing.srmock import StartResponseMock
|
||||
from falcon.util import CaseInsensitiveDict
|
||||
|
||||
|
||||
class Result(object):
|
||||
"""Encapsulates the result of a simulated WSGI request.
|
||||
|
||||
Args:
|
||||
iterable (iterable): An iterable that yields zero or more
|
||||
bytestrings, per PEP-3333
|
||||
status (str): An HTTP status string, including status code and
|
||||
reason string
|
||||
headers (list): A list of (header_name, header_value) tuples,
|
||||
per PEP-3333
|
||||
|
||||
Attributes:
|
||||
status (str): HTTP status string given in the response
|
||||
status_code (int): The code portion of the HTTP status string
|
||||
headers (CaseInsensitiveDict): A case-insensitive dictionary
|
||||
containing all the headers in the response
|
||||
encoding (str): Text encoding of the response body, or ``None``
|
||||
if the encoding can not be determined.
|
||||
content (bytes): Raw response body, or ``bytes`` if the
|
||||
response body was empty.
|
||||
text (str): Decoded response body of type ``unicode``
|
||||
under Python 2.6 and 2.7, and of type ``str`` otherwise.
|
||||
If the content type does not specify an encoding, UTF-8 is
|
||||
assumed.
|
||||
json (dict): Deserialized JSON body. Raises an error if the
|
||||
response is not JSON.
|
||||
"""
|
||||
|
||||
def __init__(self, iterable, status, headers):
|
||||
self._text = None
|
||||
|
||||
self._content = b''.join(iterable)
|
||||
if hasattr(iterable, 'close'):
|
||||
iterable.close()
|
||||
|
||||
self._status = status
|
||||
self._status_code = int(status[:3])
|
||||
self._headers = CaseInsensitiveDict(headers)
|
||||
|
||||
self._encoding = get_encoding_from_headers(self._headers)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
return self._status_code
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
return self._headers
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
return self._encoding
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
return self._content
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
if self._text is None:
|
||||
if not self.content:
|
||||
self._text = u''
|
||||
else:
|
||||
if self.encoding is None:
|
||||
encoding = 'UTF-8'
|
||||
else:
|
||||
encoding = self.encoding
|
||||
|
||||
self._text = self.content.decode(encoding)
|
||||
|
||||
return self._text
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
return json.loads(self.text)
|
||||
from falcon.testing import client
|
||||
from falcon.testing.client import Result # NOQA - hoist for backwards compat
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
@@ -128,10 +43,14 @@ class TestCase(unittest.TestCase):
|
||||
:py:class:`testtools.TestCase`.
|
||||
|
||||
Attributes:
|
||||
api_class (class): An API class to use when instantiating
|
||||
the ``api`` instance (default: :py:class:`falcon.API`)
|
||||
api (object): An API instance to target when simulating
|
||||
requests (default: ``self.api_class()``)
|
||||
api_class (class): An API class or factory method to use when
|
||||
instantiating the ``api`` instance (default:
|
||||
:py:class:`falcon.API`).
|
||||
api (object): An API instance to target when simulating requests
|
||||
(default: ``self.api_class()``). When testing your
|
||||
application, you will need to overwrite this with your own
|
||||
instance of ``falcon.API``, or use `api_class` to specify a
|
||||
factory method for your application.
|
||||
"""
|
||||
|
||||
api_class = None
|
||||
@@ -168,7 +87,7 @@ class TestCase(unittest.TestCase):
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return self.simulate_request('GET', path, **kwargs)
|
||||
return client.simulate_get(self.api, path, **kwargs)
|
||||
|
||||
def simulate_head(self, path='/', **kwargs):
|
||||
"""Simulates a HEAD request to a WSGI application.
|
||||
@@ -184,7 +103,7 @@ class TestCase(unittest.TestCase):
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return self.simulate_request('HEAD', path, **kwargs)
|
||||
return client.simulate_head(self.api, path, **kwargs)
|
||||
|
||||
def simulate_post(self, path='/', **kwargs):
|
||||
"""Simulates a POST request to a WSGI application.
|
||||
@@ -204,7 +123,7 @@ class TestCase(unittest.TestCase):
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return self.simulate_request('POST', path, **kwargs)
|
||||
return client.simulate_post(self.api, path, **kwargs)
|
||||
|
||||
def simulate_put(self, path='/', **kwargs):
|
||||
"""Simulates a PUT request to a WSGI application.
|
||||
@@ -224,7 +143,7 @@ class TestCase(unittest.TestCase):
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return self.simulate_request('PUT', path, **kwargs)
|
||||
return client.simulate_put(self.api, path, **kwargs)
|
||||
|
||||
def simulate_options(self, path='/', **kwargs):
|
||||
"""Simulates an OPTIONS request to a WSGI application.
|
||||
@@ -240,7 +159,7 @@ class TestCase(unittest.TestCase):
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return self.simulate_request('OPTIONS', path, **kwargs)
|
||||
return client.simulate_options(self.api, path, **kwargs)
|
||||
|
||||
def simulate_patch(self, path='/', **kwargs):
|
||||
"""Simulates a PATCH request to a WSGI application.
|
||||
@@ -260,7 +179,7 @@ class TestCase(unittest.TestCase):
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
"""
|
||||
return self.simulate_request('PATCH', path, **kwargs)
|
||||
return client.simulate_patch(self.api, path, **kwargs)
|
||||
|
||||
def simulate_delete(self, path='/', **kwargs):
|
||||
"""Simulates a DELETE request to a WSGI application.
|
||||
@@ -276,64 +195,15 @@ class TestCase(unittest.TestCase):
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
"""
|
||||
return self.simulate_request('DELETE', path, **kwargs)
|
||||
return client.simulate_delete(self.api, path, **kwargs)
|
||||
|
||||
def simulate_request(self, method='GET', path='/', query_string=None,
|
||||
headers=None, body=None, file_wrapper=None):
|
||||
def simulate_request(self, *args, **kwargs):
|
||||
"""Simulates a request to a WSGI application.
|
||||
|
||||
Performs a WSGI request directly against ``self.api``.
|
||||
Wraps :py:meth:`falcon.testing.simulate_request` to perform a
|
||||
WSGI request directly against ``self.api``. Equivalent to::
|
||||
|
||||
Keyword Args:
|
||||
method (str): The HTTP method to use in the request
|
||||
(default: 'GET')
|
||||
path (str): The URL path to request (default: '/')
|
||||
query_string (str): A raw query string to include in the
|
||||
request (default: ``None``)
|
||||
headers (dict): Additional headers to include in the request
|
||||
(default: ``None``)
|
||||
body (str): A string to send as the body of the request.
|
||||
Accepts both byte strings and Unicode strings
|
||||
(default: ``None``). If a Unicode string is provided,
|
||||
it will be encoded as UTF-8 in the request.
|
||||
file_wrapper (callable): Callable that returns an iterable,
|
||||
to be used as the value for *wsgi.file_wrapper* in the
|
||||
environ (default: ``None``).
|
||||
|
||||
Returns:
|
||||
:py:class:`~.Result`: The result of the request
|
||||
falcon.testing.simulate_request(self.api, *args, **kwargs)
|
||||
"""
|
||||
|
||||
if not path.startswith('/'):
|
||||
raise ValueError("path must start with '/'")
|
||||
|
||||
if query_string and query_string.startswith('?'):
|
||||
raise ValueError("query_string should not start with '?'")
|
||||
|
||||
if '?' in path:
|
||||
# NOTE(kgriffs): We could allow this, but then we'd need
|
||||
# to define semantics regarding whether the path takes
|
||||
# precedence over the query_string. Also, it would make
|
||||
# tests less consistent, since there would be "more than
|
||||
# one...way to do it."
|
||||
raise ValueError(
|
||||
'path may not contain a query string. Please use the '
|
||||
'query_string parameter instead.'
|
||||
)
|
||||
|
||||
env = create_environ(
|
||||
method=method,
|
||||
path=path,
|
||||
query_string=(query_string or ''),
|
||||
headers=headers,
|
||||
body=body,
|
||||
file_wrapper=file_wrapper,
|
||||
)
|
||||
|
||||
srmock = StartResponseMock()
|
||||
validator = wsgiref.validate.validator(self.api)
|
||||
iterable = validator(env, srmock)
|
||||
|
||||
result = Result(iterable, srmock.status, srmock.headers)
|
||||
|
||||
return result
|
||||
return client.simulate_request(self.api, *args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user