Add the concept of a 'prefix'

A prefix is applied to all the unqualifed URLs in a suite of tests.
This is useful for testing live applications that are mounted at
different points under a wsgi server.

Fixes #46
This commit is contained in:
Chris Dent 2015-06-17 12:04:25 +01:00
parent fc40dbe8e1
commit 702710be94
6 changed files with 69 additions and 13 deletions

View File

@ -14,3 +14,12 @@ expressed directly in the YAML file or provided on the command
line::
gabbi-run [host[:port]] < /my/test.yaml
To facilitate using the same tests against the application mounted
in different locations in a WSGI server, a `prefix` may be provided
as a second argument::
gabbi-run host[:port] [prefix] < /my/test.yaml
The value of prefix will be prepended to path porition of URLs that
are not fully qualified.

View File

@ -33,6 +33,7 @@ from unittest import suite
import uuid
import six
from six.moves.urllib import parse as urlparse
import yaml
from gabbi import case
@ -59,10 +60,22 @@ class TestBuilder(type):
def build_tests(path, loader, host=None, port=8001, intercept=None,
test_loader_name=None, fixture_module=None,
response_handlers=None):
response_handlers=None, prefix=None):
"""Read YAML files from a directory to create tests.
Each YAML file represents an ordered sequence of HTTP requests.
:param path: The directory where yaml files are located.
:param loader: The TestLoader.
:param host: The host to test against. Do not used with ``intercept``.
:param port: The port to test against. Used with ``host``.
:param intercept: WSGI app factory for wsgi-intercept.
:param test_loader_name: Base name for test classes. Rarely used.
:param fixture_module: Python module containing fixture classes.
:param response_handers: ResponseHandler classes.
:type response_handlers: List of ResponseHandler classes.
:param prefix: A URL prefix for all URLs that are not fully qualified.
:rtype: TestSuite containing TestSuites (one for each YAML file).
"""
if not (bool(host) ^ bool(intercept)):
@ -93,7 +106,7 @@ def build_tests(path, loader, host=None, port=8001, intercept=None,
os.path.basename(test_file))[0])
file_suite = test_suite_from_yaml(loader, test_name, test_yaml,
path, host, port, fixture_module,
intercept)
intercept, prefix)
top_suite.addTest(file_suite)
return top_suite
@ -118,7 +131,7 @@ def test_update(orig_dict, new_dict):
def test_suite_from_yaml(loader, test_base_name, test_yaml, test_directory,
host, port, fixture_module, intercept):
host, port, fixture_module, intercept, prefix=None):
"""Generate a TestSuite from YAML data."""
file_suite = gabbi_suite.GabbiSuite()
@ -153,6 +166,12 @@ def test_suite_from_yaml(loader, test_base_name, test_yaml, test_directory,
raise AssertionError('Test url missing in test %s.'
% test_name)
if prefix:
# Only add prefix to urls that have no scheme or netloc
parsed_url = urlparse.urlsplit(test['url'])
if not (parsed_url[0] and parsed_url[1]):
test['url'] = '%s%s' % (prefix, test['url'])
test_key_set = set(test.keys())
if test_key_set != base_test_key_set:
raise AssertionError(

View File

@ -36,6 +36,11 @@ def run():
gabbi-run example.com:9999 < mytest.yaml
If is also possible to provide a URL prefix which can be useful if the
target application might be mounted in different locations. An example:
gabbi-run example.com:9999 /mountpoint < mytest.yaml
Output is formatted as unittest summary information.
"""
try:
@ -47,6 +52,12 @@ def run():
port = None
except IndexError:
host, port = 'stub', None
try:
prefix = sys.argv[2]
except IndexError:
prefix = None
loader = unittest.defaultTestLoader
# Initialize the extensions for response handling.
@ -55,7 +66,8 @@ def run():
data = yaml.safe_load(sys.stdin.read())
suite = driver.test_suite_from_yaml(loader, 'input', data, '.',
host, port, None, None)
host, port, None, None,
prefix=prefix)
result = unittest.TextTestRunner(verbosity=2).run(suite)
sys.exit(not result.wasSuccessful())

View File

@ -30,16 +30,30 @@ class DriverTest(unittest.TestCase):
self.loader = unittest.defaultTestLoader
self.test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
def test_driver_loads_one_test(self):
def test_driver_loads_two_tests(self):
suite = driver.build_tests(self.test_dir, self.loader,
host='localhost', port=8001)
self.assertEqual(1, len(suite._tests),
'top level suite contains one suite')
self.assertEqual(1, len(suite._tests[0]._tests),
'contained suite contains one test')
self.assertEqual('test_driver_single_one',
suite._tests[0]._tests[0].__class__.__name__,
self.assertEqual(2, len(suite._tests[0]._tests),
'contained suite contains two tests')
the_one_test = suite._tests[0]._tests[0]
self.assertEqual('test_driver_sample_one',
the_one_test.__class__.__name__,
'test class name maps')
self.assertEqual('one',
the_one_test.test_data['name'])
self.assertEqual('/', the_one_test.test_data['url'])
def test_driver_prefix(self):
suite = driver.build_tests(self.test_dir, self.loader,
host='localhost', port=8001,
prefix='/mountpoint')
the_one_test = suite._tests[0]._tests[0]
the_two_test = suite._tests[0]._tests[1]
self.assertEqual('/mountpoint/', the_one_test.test_data['url'])
self.assertEqual('http://example.com/moo',
the_two_test.test_data['url'])
def test_build_requires_host_or_intercept(self):
with self.assertRaises(AssertionError):

View File

@ -0,0 +1,6 @@
tests:
- name: one
url: /
- name: two
url: http://example.com/moo

View File

@ -1,4 +0,0 @@
tests:
- name: one
url: /