Skip to content

Commit

Permalink
Merge pull request #18 from gocept/inheritance-based-layer
Browse files Browse the repository at this point in the history
Add another Layer to combine with `zope.app.wsgi.testlayer.BrowserLayer`
  • Loading branch information
florianpilz committed Sep 30, 2016
2 parents fc951a0 + 77e0ef5 commit 82f3251
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 13 deletions.
21 changes: 13 additions & 8 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ CHANGES

Example: if your test file looked like this ::

# my/package/tests/test_all.py
# my/package/tests.py
from zope.app.testing.functional import defineLayer
from zope.app.testing.functional import FunctionalDocFileSuite
defineLayer('MyFtestLayer', 'ftesting.zcml', allow_teardown=True)
Expand All @@ -41,17 +41,22 @@ CHANGES

now you'll have to use ::

# my/package/tests/test_all.py
# my/package/tests.py
from unittest import TestSuite
import doctest
from zope.app.wsgi.testlayer import BrowserLayer
import my.package.tests
import zope.app.wsgi.testlayer
import zope.testbrowser.wsgi

MyFtestLayer = BrowserLayer(my.package.tests, 'ftesting.zcml')
class Layer(zope.testbrowser.wsgi.TestBrowserLayer,
zope.app.wsgi.testlayer.BrowserLayer):
"""Layer to prepare zope.testbrowser using the WSGI app."""

layer = Layer(my.package)

def test_suite():
suite = doctest.DocFileSuite('test.txt', ...)
suite.layer = MyFtestLayer
return suite
suite.layer = layer
return TestSuite((suite,))

and then change all your tests from ::

Expand Down Expand Up @@ -251,7 +256,7 @@ CHANGES
3.7.0a1 (2009-08-29)
--------------------

- Update dependency from ``zope.app.publisher`` to
- Update dependency from ``zope.app.publisher`` to
``zope.browserpage``, ``zope.browserresource`` and ``zope.ptresource``.

- Remove dependencies on ``zope.app.principalannotation`` and
Expand Down
16 changes: 15 additions & 1 deletion docs/narrative.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,24 @@ Example when using the layer:
... def make_wsgi_app(self):
... return zope.testbrowser.wsgi.AuthorizationMiddleware(simple_app)

There is also a BrowserLayer in `zope.app.wsgi.testlayer`_ which does this
There is also a ``BrowserLayer`` in `zope.app.wsgi.testlayer`_ which does this
for you and includes a ``TransactionMiddleware``, too, which could be handy
when testing a ZODB based application.

However, since the ``BrowserLayer`` in `zope.app.wsgi.testlayer`_ re-creates
the ZODB in ``testSetUp``, we need to re-create the WSGI App during
``testSetUp``, too. Therefore use ``TestBrowserLayer`` of
``zope.testbrowser.wsgi`` instead of the simpler ``Layer`` to combine it with
the ``BrowserLayer`` in `zope.app.wsgi.testlayer`_:

.. doctest::

>>> import zope.testbrowser.wsgi
>>> import zope.app.wsgi.testlayer
>>> class Layer(zope.testbrowser.wsgi.TestBrowserLayer,
... zope.app.wsgi.testlayer.BrowserLayer):
... pass

.. _`zope.app.wsgi.testlayer` : http://pypi.python.org/pypi/zope.app.wsgi
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

long_description = (README + '\n\n' + CHANGES)

tests_require = ['zope.testing']
tests_require = ['zope.testing', 'mock']

setup(
name='zope.testbrowser',
Expand Down
65 changes: 64 additions & 1 deletion src/zope/testbrowser/tests/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#
##############################################################################

import contextlib
import mock
import unittest

import zope.testbrowser.wsgi
Expand All @@ -29,6 +31,25 @@ def make_wsgi_app(self):
SIMPLE_LAYER = SimpleLayer()


class AppLayer(object):

def make_wsgi_app(self):
return demo_app

def testSetUp(self):
"""Stub to mock it in test to check it was called."""

def testTearDown(self):
"""Stub to mock it in test to check it was called."""


class TestBrowserLayer(zope.testbrowser.wsgi.TestBrowserLayer, AppLayer):
"""Prepare `_APP_UNDER_TEST` with `make_wsgi_app` from `AppLayer`."""


TEST_BROWSER_LAYER = TestBrowserLayer()


class TestBrowser(unittest.TestCase):

def test_redirect_and_raiseHttpErrors(self):
Expand Down Expand Up @@ -182,7 +203,7 @@ def test_binary_content_type(self):
self.assertEqual(browser.headers['content-type'], 'image/gif')


class TestWSGILayer(unittest.TestCase):
class TestLayer(unittest.TestCase):

def setUp(self):
# test the layer without depending on zope.testrunner
Expand Down Expand Up @@ -210,6 +231,48 @@ def test_there_can_only_be_one(self):
self.assertRaises(AssertionError, another_layer.setUp)


class TestTestBrowserLayer(unittest.TestCase):

@contextlib.contextmanager
def wsgi_layer(self):
TEST_BROWSER_LAYER.testSetUp()
yield
TEST_BROWSER_LAYER.testTearDown()

def test_layer(self):
"""When the layer is setup, the wsgi_app argument is unnecessary"""
with self.wsgi_layer():
browser = zope.testbrowser.wsgi.Browser()
browser.open('http://localhost')
self.assertTrue(browser.contents.startswith('Hello world!\n'))

def test_there_can_only_be_one(self):
with self.wsgi_layer():
another_layer = TestBrowserLayer()
self.assertRaises(AssertionError, another_layer.testSetUp)

def test_supports_multiple_inheritance(self):
with mock.patch('zope.testbrowser.tests.test_wsgi'
'.AppLayer.testSetUp') as testSetUp:
TEST_BROWSER_LAYER.testSetUp()
self.assertEqual(1, testSetUp.call_count)
with mock.patch('zope.testbrowser.tests.test_wsgi'
'.AppLayer.testTearDown') as testTearDown:
TEST_BROWSER_LAYER.testTearDown()
self.assertEqual(1, testTearDown.call_count)

def test_raise_error_when_make_wsgi_app_is_not_implemented(self):
with self.assertRaises(NotImplementedError):
zope.testbrowser.wsgi.TestBrowserLayer().testSetUp()

def test_do_not_raise_error_when_make_wsgi_app_returns_None(self):
with mock.patch('zope.testbrowser.tests.test_wsgi'
'.AppLayer.make_wsgi_app') as make_wsgi_app:
make_wsgi_app.return_value = None
TEST_BROWSER_LAYER.testSetUp()
TEST_BROWSER_LAYER.testTearDown()


class TestAuthorizationMiddleware(unittest.TestCase):

def setUp(self):
Expand Down
50 changes: 48 additions & 2 deletions src/zope/testbrowser/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ def application_start_response(status, headers, exc_info=None):


class Layer(object):
"""Test layer which sets up WSGI application for use with
WebTest/testbrowser.
"""Test layer which sets up WSGI app for use with WebTest/testbrowser.
Inherit from this layer and overwrite `make_wsgi_app` for setup.
Composing multiple layers into one is supported using plone.testing.Layer.
"""

Expand Down Expand Up @@ -122,3 +125,46 @@ def tearDown(self):
global _APP_UNDER_TEST
_APP_UNDER_TEST = None
self.cooperative_super('tearDown')


class TestBrowserLayer(object):
"""Test layer which sets up WSGI app for use with WebTest/testbrowser.
This layer is intended for use cases, where `make_wsgi_app` is implemented
by another class using multiple inheritance.
We used `testSetUp` and `testTearDown` instead of `setUp` and `tearDown` to
cooperate with layers from other zope packages, e.g.
`zope.app.wsgi.testlayer.BrowserLayer`, since they re-create the DB
connection during `testSetUp`. Therefore we need to re-create the app, too.
Make sure this layer always comes first in multiple inheritance, because
the requirements of other layers should be set up before calling
`make_wsgi_app`. In addition, many layers do not make sure to call multiple
superclasses using something like `cooperative_super`, thus the methods of
this layer may not be called if it comes later.
"""

def cooperative_super(self, method_name):
# Calling `super` for multiple inheritance:
method = getattr(super(TestBrowserLayer, self), method_name, None)
if method is not None:
return method()

def make_wsgi_app(self):
if not hasattr(super(TestBrowserLayer, self), 'make_wsgi_app'):
raise NotImplementedError
return super(TestBrowserLayer, self).make_wsgi_app()

def testSetUp(self):
self.cooperative_super('testSetUp')
global _APP_UNDER_TEST
if _APP_UNDER_TEST is not None:
raise AssertionError("Already Setup")
_APP_UNDER_TEST = self.make_wsgi_app()

def testTearDown(self):
global _APP_UNDER_TEST
_APP_UNDER_TEST = None
self.cooperative_super('testTearDown')
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ commands =
deps =
Sphinx
repoze.sphinx.autointerface
zope.app.wsgi

[testenv:flake8]
basepython = python2
Expand Down

0 comments on commit 82f3251

Please sign in to comment.