changeset 2689:aaf385f07d0a

upgrade tests to py.test 2.5/2.6, thanks to dimazest!
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 02 Aug 2014 21:08:11 +0200
parents c040fb080073
children 86e3b39c4b02
files MoinMoin/_tests/test_test_environ.py MoinMoin/_tests/test_user.py MoinMoin/apps/admin/_tests/test_admin.py MoinMoin/apps/feed/_tests/test_feed.py MoinMoin/apps/frontend/_tests/test_frontend.py MoinMoin/apps/misc/_tests/test_misc.py MoinMoin/apps/serve/_tests/test_serve.py MoinMoin/auth/_tests/test_auth.py MoinMoin/auth/_tests/test_http.py MoinMoin/conftest.py MoinMoin/converter/_tests/test_link.py MoinMoin/datastruct/backends/_tests/test_composite_dicts.py MoinMoin/datastruct/backends/_tests/test_composite_groups.py MoinMoin/datastruct/backends/_tests/test_config_dicts.py MoinMoin/datastruct/backends/_tests/test_config_groups.py MoinMoin/datastruct/backends/_tests/test_lazy_config_groups.py MoinMoin/datastruct/backends/_tests/test_wiki_dicts.py MoinMoin/datastruct/backends/_tests/test_wiki_groups.py MoinMoin/items/_tests/test_Blog.py MoinMoin/macro/_tests/test_GetVal.py MoinMoin/security/_tests/test_security.py MoinMoin/security/_tests/test_textcha.py MoinMoin/storage/middleware/_tests/test_indexing.py MoinMoin/storage/middleware/_tests/test_protecting.py MoinMoin/themes/_tests/test_navi_bar.py MoinMoin/util/_tests/test_interwiki.py MoinMoin/util/_tests/test_notifications.py MoinMoin/util/_tests/test_pysupport.py MoinMoin/util/_tests/test_subscriptions.py setup.py
diffstat 30 files changed, 535 insertions(+), 540 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/test_test_environ.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/_tests/test_test_environ.py	Sat Aug 02 21:08:11 2014 +0200
@@ -14,6 +14,8 @@
 
 from MoinMoin._tests import wikiconfig
 
+import pytest
+
 
 class TestStorageEnvironWithoutConfig(object):
     def setup_method(self, method):
@@ -44,8 +46,12 @@
 
 class TestStorageEnvironWithConfig(object):
 
-    class Config(wikiconfig.Config):
-        default_acl = DEFAULT_ACL
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            default_acl = DEFAULT_ACL
+
+        return Config
 
     def test_config(self):
         assert isinstance(app.cfg, wikiconfig.Config)
--- a/MoinMoin/_tests/test_user.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/_tests/test_user.py	Sat Aug 02 21:08:11 2014 +0200
@@ -15,6 +15,8 @@
 from MoinMoin.items import Item
 from MoinMoin.constants.keys import (ITEMID, NAME, NAMEPREFIX, NAMERE, NAMESPACE, TAGS)
 
+import pytest
+
 
 class TestSimple(object):
     def test_create_retrieve(self):
@@ -38,20 +40,13 @@
 
 
 class TestUser(object):
-    def setup_method(self, method):
-        # Save original user
-        self.saved_user = flaskg.user
-
-        # Create anon user for the tests
-        flaskg.user = user.User()
 
-    def teardown_method(self, method):
-        """ Run after each test
-
-        Remove user and reset user listing cache.
-        """
-        # Restore original user
-        flaskg.user = self.saved_user
+    @pytest.yield_fixture(autouse=True)
+    def saved_user(self):
+        orig_user = flaskg.user
+        flaskg.user = user.User()
+        yield flaskg.user
+        flaskg.user = orig_user
 
     # Passwords / Login -----------------------------------------------
 
--- a/MoinMoin/apps/admin/_tests/test_admin.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/apps/admin/_tests/test_admin.py	Sat Aug 02 21:08:11 2014 +0200
@@ -7,33 +7,53 @@
 
 from flask import url_for
 
-
-class TestAdmin(object):
-    def _test_view_get(self, url, status='200 OK', data=('<html>', '</html>')):
-        with self.app.test_client() as c:
-            rv = c.get(url)
-            assert rv.status == status
-            assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
-            for item in data:
-                assert item in rv.data
-
-    def test_index(self):
-        self._test_view_get(url_for('admin.index'), status='403 FORBIDDEN')
+import pytest
 
-    def test_userprofile(self):
-        self._test_view_get(url_for('admin.userprofile', user_name='DoesntExist'), status='403 FORBIDDEN')
-
-    def test_wikiconfig(self):
-        self._test_view_get(url_for('admin.wikiconfig'), status='403 FORBIDDEN')
 
-    def test_wikiconfighelp(self):
-        self._test_view_get(url_for('admin.wikiconfighelp'), status='403 FORBIDDEN')
-
-    def test_interwikihelp(self):
-        self._test_view_get(url_for('admin.interwikihelp'))
-
-    def test_highlighterhelp(self):
-        self._test_view_get(url_for('admin.highlighterhelp'))
-
-    def test_itemsize(self):
-        self._test_view_get(url_for('admin.itemsize'))
+@pytest.mark.parametrize(
+    'url_for_args,status,data',
+    (
+        (
+            {'endpoint': 'admin.index'},
+            '403 FORBIDDEN',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.userprofile', 'user_name': 'DoesntExist'},
+            '403 FORBIDDEN',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.wikiconfig'},
+            '403 FORBIDDEN',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.wikiconfighelp'},
+            '403 FORBIDDEN',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.interwikihelp'},
+            '200 OK',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.highlighterhelp'},
+            '200 OK',
+            ('<html>', '</html>'),
+        ),
+        (
+            {'endpoint': 'admin.itemsize'},
+            '200 OK',
+            ('<html>', '</html>'),
+        ),
+    ),
+)
+def test_admin(app, url_for_args, status, data):
+    with app.test_client() as c:
+        rv = c.get(url_for(**url_for_args))
+        assert rv.status == status
+        assert rv.headers['Content-Type'] == 'text/html; charset=utf-8'
+        for item in data:
+            assert item in rv.data
--- a/MoinMoin/apps/feed/_tests/test_feed.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/apps/feed/_tests/test_feed.py	Sat Aug 02 21:08:11 2014 +0200
@@ -8,17 +8,12 @@
 from flask import url_for
 
 from MoinMoin.constants.keys import COMMENT
-from MoinMoin._tests import update_item, wikiconfig
+from MoinMoin._tests import update_item
 
 
 class TestFeeds(object):
-    class Config(wikiconfig.Config):
-        """
-        we just have this so the test framework creates a new app with empty backends for us.
-        """
-
-    def test_global_atom(self):
-        with self.app.test_client() as c:
+    def test_global_atom(self, app):
+        with app.test_client() as c:
             rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
@@ -26,10 +21,10 @@
             assert '<feed xmlns="http://www.w3.org/2005/Atom">' in rv.data
             assert '</feed>' in rv.data
 
-    def test_global_atom_with_an_item(self):
+    def test_global_atom_with_an_item(self, app):
         basename = u'Foo'
-        item = update_item(basename, {COMMENT: u"foo data for feed item"}, '')
-        with self.app.test_client() as c:
+        update_item(basename, {COMMENT: u"foo data for feed item"}, '')
+        with app.test_client() as c:
             rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
@@ -38,7 +33,7 @@
 
         # tests the cache invalidation
         update_item(basename, {COMMENT: u"checking if the cache invalidation works"}, '')
-        with self.app.test_client() as c:
+        with app.test_client() as c:
             rv = c.get(url_for('feed.atom'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'application/atom+xml'
--- a/MoinMoin/apps/frontend/_tests/test_frontend.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/apps/frontend/_tests/test_frontend.py	Sat Aug 02 21:08:11 2014 +0200
@@ -15,8 +15,15 @@
 from MoinMoin.apps.frontend import views
 from MoinMoin import user
 
+import pytest
+
 
 class TestFrontend(object):
+
+    @pytest.fixture(autouse=True)
+    def custom_setup(self, app):
+        self.app = app
+
     def _test_view(self, viewname, status='200 OK', data=('<html>', '</html>'), content_types=('text/html; charset=utf-8', ), viewopts=None, params=None):
         if viewopts is None:
             viewopts = {}
@@ -216,26 +223,12 @@
 class TestUsersettings(object):
     reinit_storage = True  # avoid username / email collisions
 
-    def setup_method(self, method):
-        # Save original user
-        self.saved_user = flaskg.user
-
-        # Create anon user for the tests
+    @pytest.yield_fixture(autouse=True)
+    def custom_setup(self, app):
+        saved_user = flaskg.user
         flaskg.user = user.User()
-
-        self.user = None
-
-    def teardown_method(self, method):
-        """ Run after each test
-
-        Remove user and reset user listing cache.
-        """
-        # Remove user file and user
-        if self.user is not None:
-            del self.user
-
-        # Restore original user
-        flaskg.user = self.saved_user
+        yield
+        flaskg.user = saved_user
 
     def test_user_password_change(self):
         self.createUser(u'moin', u'Xiwejr622')
--- a/MoinMoin/apps/misc/_tests/test_misc.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/apps/misc/_tests/test_misc.py	Sat Aug 02 21:08:11 2014 +0200
@@ -7,17 +7,10 @@
 
 from flask import url_for
 
-from MoinMoin._tests import wikiconfig
-
 
 class TestMisc(object):
-    class Config(wikiconfig.Config):
-        """
-        we just have this so the test framework creates a new app with empty backends for us.
-        """
-
-    def test_global_sitemap(self):
-        with self.app.test_client() as c:
+    def test_global_sitemap(self, app):
+        with app.test_client() as c:
             rv = c.get(url_for('misc.sitemap'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'text/xml; charset=utf-8'
@@ -25,8 +18,8 @@
             assert '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' in rv.data
             assert '</urlset>' in rv.data
 
-    def test_urls_names(self):
-        with self.app.test_client() as c:
+    def test_urls_names(self, app):
+        with app.test_client() as c:
             rv = c.get(url_for('misc.urls_names'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'text/plain; charset=utf-8'
--- a/MoinMoin/apps/serve/_tests/test_serve.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/apps/serve/_tests/test_serve.py	Sat Aug 02 21:08:11 2014 +0200
@@ -9,14 +9,14 @@
 
 
 class TestServe(object):
-    def test_index(self):
-        with self.app.test_client() as c:
+    def test_index(self, app):
+        with app.test_client() as c:
             rv = c.get(url_for('serve.index'))
             assert rv.status == '200 OK'
             assert rv.headers['Content-Type'] == 'text/plain'
 
-    def test_files(self):
-        with self.app.test_client() as c:
+    def test_files(self, app):
+        with app.test_client() as c:
             rv = c.get(url_for('serve.files', name="DoesntExist"))
             assert rv.status == '404 NOT FOUND'
             assert rv.headers['Content-Type'] == 'text/html'
--- a/MoinMoin/auth/_tests/test_auth.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/auth/_tests/test_auth.py	Sat Aug 02 21:08:11 2014 +0200
@@ -12,11 +12,17 @@
 from MoinMoin.auth import GivenAuth, handle_login, get_multistage_continuation_url
 from MoinMoin.user import create_user
 
+import pytest
+
 
 class TestConfiguredGivenAuth(object):
     """ Test: configured GivenAuth """
-    class Config(wikiconfig.Config):
-        auth = [GivenAuth(user_name=u'JoeDoe', autocreate=True), ]
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            auth = [GivenAuth(user_name=u'JoeDoe', autocreate=True)]
+
+        return Config
 
     def test(self):
         assert flaskg.user.name == [u'JoeDoe', ]
--- a/MoinMoin/auth/_tests/test_http.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/auth/_tests/test_http.py	Sat Aug 02 21:08:11 2014 +0200
@@ -6,28 +6,32 @@
 """
 
 from flask import g as flaskg
-from flask import request
+from flask import request as flask_request
 
 from MoinMoin.user import create_user
 from MoinMoin.auth.http import HTTPAuthMoin
 from MoinMoin.constants.misc import ANON
 
+import pytest
+
 
 class TestHTTPAuthMoin(object):
     """ Test: HTTPAuthMoin """
 
-    class Auth(object):
-        def __init__(self):
-            self.username = 'ValidUser'
-            self.password = 'test_pass'
+    @pytest.yield_fixture(autouse=True)
+    def custom_setup(self):
+        class Auth(object):
+            def __init__(self):
+                self.username = 'ValidUser'
+                self.password = 'test_pass'
 
-    def setup_method(self, method):
         flaskg.user.auth_method = 'http'
-        request.authorization = self.Auth()
+        flask_request.authorization = Auth()
 
-    def teardown_method(self, method):
+        yield
+
         flaskg.user.auth_method = 'invalid'
-        request.authorization = None
+        flask_request.authorization = None
 
     def test_request(self):
         # create a new user
--- a/MoinMoin/conftest.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/conftest.py	Sat Aug 02 21:08:11 2014 +0200
@@ -24,17 +24,14 @@
     '../wiki',  # no tests there
     '../instance',  # tw likes to use this for wiki data (non-revisioned)
 ]
-import atexit
-import os
-import sys
-import inspect
 
 import pytest
 import py
 import MoinMoin.log
+import MoinMoin
 
 # Logging for tests to avoid useless output like timing information on stderr on test failures
-Moindir = py.path.local(__file__).dirname
+Moindir = py.path.local(MoinMoin.__file__).dirname
 config_file = Moindir + '/_tests/test_logging.conf'
 MoinMoin.log.load_config(config_file)
 
@@ -43,9 +40,17 @@
 from MoinMoin.storage import create_simple_mapping
 
 
-def init_test_app(given_config):
-    namespace_mapping, backend_mapping, acl_mapping = \
-        create_simple_mapping("stores:memory:", given_config.default_acl)
+@pytest.fixture
+def cfg():
+    return wikiconfig.Config
+
+
+@pytest.yield_fixture
+def app_ctx(cfg):
+    namespace_mapping, backend_mapping, acl_mapping = create_simple_mapping(
+        "stores:memory:",
+        cfg.default_acl
+    )
     more_config = dict(
         namespace_mapping=namespace_mapping,
         backend_mapping=backend_mapping,
@@ -55,76 +60,31 @@
         create_index=True,  # create a fresh index at each app start
         destroy_index=True,  # kill index contents at app shutdown
     )
-    app = create_app_ext(flask_config_dict=dict(SECRET_KEY='foobarfoobar'),
-                         moin_config_class=given_config,
-                         **more_config)
+    app = create_app_ext(
+        flask_config_dict=dict(SECRET_KEY='foobarfoobar'),
+        moin_config_class=cfg,
+        **more_config
+    )
     ctx = app.test_request_context('/', base_url="http://localhost:8080/")
     ctx.push()
     before_wiki()
-    return app, ctx
 
+    yield app, ctx
 
-def deinit_test_app(app, ctx):
     teardown_wiki('')
     ctx.pop()
     destroy_app(app)
 
 
-# we keep state in these globals:
-prev_app = None
-prev_ctx = None
-prev_cfg = None
-
-
-class MoinTestFunction(pytest.collect.Function):
-    def setup(self):
-        global prev_app, prev_ctx, prev_cfg
-
-        if inspect.isclass(self.parent.obj.__class__):
-            cls = self.parent.obj.__class__
-            cfg = getattr(cls, 'Config', wikiconfig.Config)
-            reinit = getattr(cls, 'reinit_storage', False)
-            if (prev_cfg is not cfg or reinit) and prev_app is not None:
-                # other config, previous app exists, so deinit it:
-                deinit_test_app(prev_app, prev_ctx)
-            if prev_cfg is not cfg or reinit or prev_app is None:
-                # other config or no app yet, init app:
-                self.app, self.ctx = init_test_app(cfg)
-            else:
-                # otherwise continue using the app/ctx we have:
-                self.app = prev_app
-                self.ctx = prev_ctx
-            # remember what we have, for next setup()
-            prev_app, prev_ctx, prev_cfg = self.app, self.ctx, cfg
-        else:
-            prev_app, prev_ctx, prev_cfg = None, None, None
-
-        super(MoinTestFunction, self).setup()
-        # XXX: hack till we get better funcarg tools
-        if hasattr(self._obj, 'im_self'):
-            self._obj.im_self.app = self.app
-
-    def teardown(self):
-        super(MoinTestFunction, self).teardown()
+@pytest.fixture(autouse=True)
+def app(app_ctx):
+    return app_ctx[0]
 
 
 def pytest_pycollect_makemodule(path, parent):
     return Module(path, parent=parent)
 
 
-def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
-    if collector.funcnamefilter(name) and inspect.isfunction(obj):
-        return MoinTestFunction(name, parent=collector)
-
-
-def pytest_pyfunc_call(pyfuncitem):
-    """hook to intercept generators and run them as a single test items"""
-    if inspect.isgeneratorfunction(pyfuncitem.obj):
-        for item in pyfuncitem.obj():
-            kwarg = item[1:]
-            item[0](*kwarg)
-
-
 def pytest_report_header(config):
     return "The tests here are implemented only for pytest-2"
 
--- a/MoinMoin/converter/_tests/test_link.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/converter/_tests/test_link.py	Sat Aug 02 21:08:11 2014 +0200
@@ -5,159 +5,128 @@
 MoinMoin - Tests for MoinMoin.converter.link
 """
 
+from emeraldtree import tree as ET
+
+from MoinMoin.converter.link import ConverterExternOutput, xlink, ConverterItemRefs
+from MoinMoin.util.iri import Iri
 
 import pytest
 
-from emeraldtree import tree as ET
 
-from flask import current_app as app
-
-from MoinMoin.converter.link import *
-from MoinMoin.util.iri import Iri
+@pytest.fixture
+def conv():
+    return ConverterExternOutput()
 
 
-class TestConverterExternOutput(object):
-    def setup_class(self):
-        self.conv = ConverterExternOutput()
-
-    def test_wiki(self):
-        assert 'MoinMoin' in app.cfg.interwiki_map
-        pairs = [
-            # note: result URLs assume test wiki running at /
-            ('wiki:///Test',
-                '/Test'),
-            ('wiki:///Test?mode=raw',
-                '/Test?mode=raw'),
-            ('wiki:///Test#anchor',
-                '/Test#anchor'),
-            ('wiki:///Test?mode=raw#anchor',
-                '/Test?mode=raw#anchor'),
-            ('wiki://MoinMoin/Test',
-                'http://moinmo.in/Test'),
-        ]
-        for i in pairs:
-            yield (self._do_wiki, ) + i
+@pytest.mark.parametrize(
+    'input_,output',
+    (
+        ('wiki:///Test', '/Test'),
+        ('wiki:///Test?mode=raw', '/Test?mode=raw'),
+        ('wiki:///Test#anchor', '/Test#anchor'),
+        ('wiki:///Test?mode=raw#anchor', '/Test?mode=raw#anchor'),
+        ('wiki://MoinMoin/Test', 'http://moinmo.in/Test'),
+    ),
+)
+def test_wiki(app, conv, input_, output):
+    assert 'MoinMoin' in app.cfg.interwiki_map
 
-    def test_wikilocal(self):
-        pairs = [
-            # note: result URLs assume test wiki running at /
-            ('wiki.local:',
-                'wiki:///Root',
-                '/Root'),
-            ('wiki.local:Test',
-                'wiki:///Root',
-                '/Test'),
-            ('wiki.local:Test',
-                'wiki:///Root/Sub',
-                '/Test'),
-            ('wiki.local:/Test',
-                'wiki:///Root',
-                '/Root/Test'),
-            ('wiki.local:/Test',
-                'wiki:///Root/Sub',
-                '/Root/Sub/Test'),
-            ('wiki.local:../Test',
-                'wiki:///Root',
-                '/Test'),
-            ('wiki.local:../Test',
-                'wiki:///Root/Sub',
-                '/Root/Test'),
-        ]
-        for i in pairs:
-            yield (self._do_wikilocal, ) + i
-
-    def test_wikiexternal(self):
-        pairs = [
-            ('http://moinmo.in/',
-             'http://moinmo.in/'),
-            ('mailto:foo.bar@example.org',
-             'mailto:foo.bar@example.org'),
-        ]
-        for i in pairs:
-            yield (self._do_wikiexternal, ) + i
-
-    def _do_wiki(self, input, output, skip=None):
-        if skip:
-            pytest.skip(skip)
-        elem = ET.Element(None)
-        self.conv.handle_wiki_links(elem, Iri(input))
-        assert elem.get(xlink.href) == output
-
-    def _do_wikilocal(self, input, page, output, skip=None):
-        if skip:
-            pytest.skip(skip)
-        elem = ET.Element(None)
-        self.conv.handle_wikilocal_links(elem, Iri(input), Iri(page))
-        assert elem.get(xlink.href) == output
-
-    def _do_wikiexternal(self, input, output, skip=None):
-        if skip:
-            pytest.skip(skip)
-        elem = ET.Element(None)
-        self.conv.handle_external_links(elem, Iri(input))
-        href = elem.get(xlink.href)
-        assert href == output
+    elem = ET.Element(None)
+    conv.handle_wiki_links(elem, Iri(input_))
+    assert elem.get(xlink.href) == output
 
 
-class TestConverterRefs(object):
-    def setup_class(self):
-        self.converter = ConverterItemRefs()
-
-    def testItems(self):
-        tree_xml = u"""
-        <ns0:page ns0:page-href="wiki:///Home" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/2001/XInclude" xmlns:ns2="http://www.w3.org/1999/xlink">
-        <ns0:body><ns0:p><ns1:include ns1:href="wiki.local:moin_transcluded?" />
-        <ns1:include ns1:href="wiki.local:moin2_transcluded?" />
-        <ns0:a ns2:href="wiki.local:moin_linked">moin_linked</ns0:a>
-        <ns0:a ns2:href="wiki.local:moin2_linked">moin2_linked</ns0:a></ns0:p>
-        <ns0:p>safas\nafsfasfas\nfas\nfassaf</ns0:p>
-        <ns0:p><ns1:include ns1:href="wiki.local:moin_transcluded?" />
-        <ns1:include ns1:href="wiki.local:moin2_transcluded?" />
-        <ns0:a ns2:href="wiki.local:moin_linked">moin_linked</ns0:a>
-        <ns0:a ns2:href="wiki.local:moin2_linked">moin2_linked</ns0:a></ns0:p></ns0:body></ns0:page>
-        """
-        transclusions_expected = [u"moin_transcluded", u"moin2_transcluded"]
-        links_expected = [u"moin_linked", u"moin2_linked"]
-        external_expected = []
-
-        self.runItemTest(tree_xml, links_expected, transclusions_expected, external_expected)
+@pytest.mark.parametrize(
+    'input_,page,output',
+    (
+        # note: result URLs assume test wiki running at /
+        ('wiki.local:', 'wiki:///Root', '/Root'),
+        ('wiki.local:Test', 'wiki:///Root', '/Test'),
+        ('wiki.local:Test', 'wiki:///Root/Sub', '/Test'),
+        ('wiki.local:/Test', 'wiki:///Root', '/Root/Test'),
+        ('wiki.local:/Test', 'wiki:///Root/Sub', '/Root/Sub/Test'),
+        ('wiki.local:../Test', 'wiki:///Root', '/Test'),
+        ('wiki.local:../Test', 'wiki:///Root/Sub', '/Root/Test'),
+    )
+)
+def test_wikilocal(conv, input_, page, output):
+    elem = ET.Element(None)
+    conv.handle_wikilocal_links(elem, Iri(input_), Iri(page))
+    assert elem.get(xlink.href) == output
 
-    def testRelativeItems(self):
-        tree_xml = u"""
-        <ns0:page ns0:page-href="wiki:///Home/Subpage" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="http://www.w3.org/2001/XInclude">
-        <ns0:body><ns0:p><ns0:a ns1:href="wiki.local:../../moin_linked">../../moin_linked</ns0:a>
-        <ns0:a ns1:href="wiki.local:/moin2_linked">/moin2_linked</ns0:a>
-        <ns2:include ns2:href="wiki.local:../../moin_transcluded?" />
-        <ns2:include ns2:href="wiki.local:/moin2_transcluded?" /></ns0:p></ns0:body></ns0:page>
-        """
-        transclusions_expected = [u"Home/Subpage/moin2_transcluded", u"moin_transcluded"]
-        links_expected = [u"moin_linked", u"Home/Subpage/moin2_linked"]
-        external_expected = []
-
-        self.runItemTest(tree_xml, links_expected, transclusions_expected, external_expected)
 
-    def testExternal(self):
-        tree_xml = u"""
-        <ns0:page ns0:page-href="wiki:///Home/Subpage" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="http://www.w3.org/2001/XInclude">
-        <ns0:body><ns0:p><ns0:a ns1:href="http://example.org/">test</ns0:a>
-        <ns0:a ns1:href="mailto:foo.bar@example.org">test</ns0:a>
-        </ns0:p></ns0:body></ns0:page>
-        """
-        transclusions_expected = []
-        links_expected = []
-        external_expected = [u"http://example.org/", u"mailto:foo.bar@example.org"]
+@pytest.mark.parametrize(
+    'input_,output',
+    (
+        ('http://moinmo.in/', 'http://moinmo.in/'),
+        ('mailto:foo.bar@example.org', 'mailto:foo.bar@example.org'),
+    )
+)
+def test_wikiexternal(conv, input_, output):
+    elem = ET.Element(None)
+    conv.handle_external_links(elem, Iri(input_))
+    href = elem.get(xlink.href)
+    assert href == output
 
-        self.runItemTest(tree_xml, links_expected, transclusions_expected, external_expected)
 
-    def runItemTest(self, tree_xml, links_expected, transclusions_expected, external_expected):
-        tree = ET.XML(tree_xml)
-        self.converter(tree)
-        links_result = self.converter.get_links()
-        transclusions_result = self.converter.get_transclusions()
-        external_result = self.converter.get_external_links()
+@pytest.mark.parametrize(
+    'tree_xml,links_expected,transclusions_expected,external_expected',
+    (
+        (
+            u"""
+            <ns0:page ns0:page-href="wiki:///Home" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/2001/XInclude" xmlns:ns2="http://www.w3.org/1999/xlink">
+            <ns0:body><ns0:p><ns1:include ns1:href="wiki.local:moin_transcluded?" />
+            <ns1:include ns1:href="wiki.local:moin2_transcluded?" />
+            <ns0:a ns2:href="wiki.local:moin_linked">moin_linked</ns0:a>
+            <ns0:a ns2:href="wiki.local:moin2_linked">moin2_linked</ns0:a></ns0:p>
+            <ns0:p>safas\nafsfasfas\nfas\nfassaf</ns0:p>
+            <ns0:p><ns1:include ns1:href="wiki.local:moin_transcluded?" />
+            <ns1:include ns1:href="wiki.local:moin2_transcluded?" />
+            <ns0:a ns2:href="wiki.local:moin_linked">moin_linked</ns0:a>
+            <ns0:a ns2:href="wiki.local:moin2_linked">moin2_linked</ns0:a></ns0:p></ns0:body></ns0:page>
+            """,
+            (u"moin_linked", u"moin2_linked"),
+            (u"moin_transcluded", u"moin2_transcluded"),
+            [],
+        ),
+        (
+            u"""
+            <ns0:page ns0:page-href="wiki:///Home/Subpage" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="http://www.w3.org/2001/XInclude">
+            <ns0:body><ns0:p><ns0:a ns1:href="wiki.local:../../moin_linked">../../moin_linked</ns0:a>
+            <ns0:a ns1:href="wiki.local:/moin2_linked">/moin2_linked</ns0:a>
+            <ns2:include ns2:href="wiki.local:../../moin_transcluded?" />
+            <ns2:include ns2:href="wiki.local:/moin2_transcluded?" /></ns0:p></ns0:body></ns0:page>
+            """,
+            (u"moin_linked", u"Home/Subpage/moin2_linked"),
+            (u"Home/Subpage/moin2_transcluded", u"moin_transcluded"),
+            [],
+        ),
+        (
+            u"""
+            <ns0:page ns0:page-href="wiki:///Home/Subpage" xmlns:ns0="http://moinmo.in/namespaces/page" xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="http://www.w3.org/2001/XInclude">
+            <ns0:body><ns0:p><ns0:a ns1:href="http://example.org/">test</ns0:a>
+            <ns0:a ns1:href="mailto:foo.bar@example.org">test</ns0:a>
+            </ns0:p></ns0:body></ns0:page>
+            """,
+            [],
+            [],
+            (u"http://example.org/", u"mailto:foo.bar@example.org"),
 
-        # sorting instead of sets
-        # so that we avoid deduplicating duplicated items in the result
-        assert sorted(links_result) == sorted(links_expected)
-        assert sorted(transclusions_result) == sorted(transclusions_expected)
-        assert sorted(external_result) == sorted(external_expected)
+        ),
+
+    ),
+)
+def test_converter_refs(tree_xml, links_expected, transclusions_expected, external_expected):
+    converter = ConverterItemRefs()
+    tree = ET.XML(tree_xml)
+
+    converter(tree)
+    links_result = converter.get_links()
+    transclusions_result = converter.get_transclusions()
+    external_result = converter.get_external_links()
+
+    # sorting instead of sets
+    # so that we avoid deduplicating duplicated items in the result
+    assert sorted(links_result) == sorted(links_expected)
+    assert sorted(transclusions_result) == sorted(transclusions_expected)
+    assert sorted(external_result) == sorted(external_expected)
--- a/MoinMoin/datastruct/backends/_tests/test_composite_dicts.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_composite_dicts.py	Sat Aug 02 21:08:11 2014 +0200
@@ -8,26 +8,30 @@
 
 
 from MoinMoin.datastruct.backends._tests import DictsBackendTest
-from MoinMoin.datastruct import ConfigDicts, CompositeDicts, DictDoesNotExistError
+from MoinMoin.datastruct import ConfigDicts, CompositeDicts
 from MoinMoin._tests import wikiconfig
-from MoinMoin import security
+
+import pytest
 
 
 class TestCompositeDict(DictsBackendTest):
 
-    class Config(wikiconfig.Config):
-
-        one_dict = {u'SomeTestDict': {u'First': u'first item',
-                                      u'text with spaces': u'second item',
-                                      u'Empty string': u'',
-                                      u'Last': u'last item'}}
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
 
-        other_dict = {u'SomeOtherTestDict': {u'One': '1',
-                                             u'Two': '2'}}
+            one_dict = {u'SomeTestDict': {u'First': u'first item',
+                                          u'text with spaces': u'second item',
+                                          u'Empty string': u'',
+                                          u'Last': u'last item'}}
 
-        def dicts(self):
-            return CompositeDicts(ConfigDicts(self.one_dict),
-                                  ConfigDicts(self.other_dict))
+            other_dict = {u'SomeOtherTestDict': {u'One': '1',
+                                                 u'Two': '2'}}
 
+            def dicts(self):
+                return CompositeDicts(ConfigDicts(self.one_dict),
+                                      ConfigDicts(self.other_dict))
+
+        return Config
 
 coverage_modules = ['MoinMoin.datastruct.backends.composite_dicts']
--- a/MoinMoin/datastruct/backends/_tests/test_composite_groups.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_composite_groups.py	Sat Aug 02 21:08:11 2014 +0200
@@ -14,50 +14,59 @@
 from MoinMoin.datastruct import ConfigGroups, CompositeGroups, GroupDoesNotExistError
 from MoinMoin._tests import wikiconfig
 
+import pytest
+
 
 class TestCompositeGroupsBackend(GroupsBackendTest):
 
-    class Config(wikiconfig.Config):
+    @pytest.fixture
+    def cfg(self):
 
-        def groups(self):
-            groups = GroupsBackendTest.test_groups
-            return CompositeGroups(ConfigGroups(groups))
+        class Config(wikiconfig.Config):
+
+            def groups(self):
+                groups = GroupsBackendTest.test_groups
+                return CompositeGroups(ConfigGroups(groups))
+
+        return Config
 
 
 class TestCompositeGroup(object):
 
-    class Config(wikiconfig.Config):
-
-        admin_group = frozenset([u'Admin', u'JohnDoe'])
-        editor_group = frozenset([u'MainEditor', u'JohnDoe'])
-        fruit_group = frozenset([u'Apple', u'Banana', u'Cherry'])
-
-        first_backend_groups = {u'AdminGroup': admin_group,
-                                u'EditorGroup': editor_group,
-                                u'FruitGroup': fruit_group}
-
-        user_group = frozenset([u'JohnDoe', u'Bob', u'Joe'])
-        city_group = frozenset([u'Bolzano', u'Riga', u'London'])
+    @pytest.fixture
+    def cfg(self):
 
-        # Suppose, someone hacked second backend and added himself to AdminGroup
-        second_admin_group = frozenset([u'TheHacker'])
+        class Config(wikiconfig.Config):
 
-        second_backend_groups = {u'UserGroup': user_group,
-                                 u'CityGroup': city_group,
-                                 # Here group name clash occurs.
-                                 # AdminGroup is defined in both
-                                 # first_backend and second_backend.
-                                 u'AdminGroup': second_admin_group}
+            admin_group = frozenset([u'Admin', u'JohnDoe'])
+            editor_group = frozenset([u'MainEditor', u'JohnDoe'])
+            fruit_group = frozenset([u'Apple', u'Banana', u'Cherry'])
 
-        def groups(self):
-            return CompositeGroups(ConfigGroups(self.first_backend_groups),
-                                   ConfigGroups(self.second_backend_groups))
+            first_backend_groups = {u'AdminGroup': admin_group,
+                                    u'EditorGroup': editor_group,
+                                    u'FruitGroup': fruit_group}
 
-    def setup_method(self, method):
-        self.groups = flaskg.groups
+            user_group = frozenset([u'JohnDoe', u'Bob', u'Joe'])
+            city_group = frozenset([u'Bolzano', u'Riga', u'London'])
+
+            # Suppose, someone hacked second backend and added himself to AdminGroup
+            second_admin_group = frozenset([u'TheHacker'])
+
+            second_backend_groups = {u'UserGroup': user_group,
+                                     u'CityGroup': city_group,
+                                     # Here group name clash occurs.
+                                     # AdminGroup is defined in both
+                                     # first_backend and second_backend.
+                                     u'AdminGroup': second_admin_group}
+
+            def groups(self):
+                return CompositeGroups(ConfigGroups(self.first_backend_groups),
+                                       ConfigGroups(self.second_backend_groups))
+
+        return Config
 
     def test_getitem(self):
-        raises(GroupDoesNotExistError, lambda: self.groups[u'NotExistingGroup'])
+        raises(GroupDoesNotExistError, lambda: flaskg.groups[u'NotExistingGroup'])
 
     def test_clashed_getitem(self):
         """
@@ -65,7 +74,7 @@
         backends. __getitem__ should return the first match (backends are
         considered in the order they are given in the backends list).
         """
-        admin_group = self.groups[u'AdminGroup']
+        admin_group = flaskg.groups[u'AdminGroup']
 
         # TheHacker added himself to the second backend, but that must not be
         # taken into consideration, because AdminGroup is defined in first
@@ -73,15 +82,15 @@
         assert u'TheHacker' not in admin_group
 
     def test_iter(self):
-        all_group_names = list(self.groups)
+        all_group_names = list(flaskg.groups)
 
         assert 5 == len(all_group_names)
         # There are no duplicates
         assert len(set(all_group_names)) == len(all_group_names)
 
     def test_contains(self):
-        assert u'UserGroup' in self.groups
-        assert u'not existing group' not in self.groups
+        assert u'UserGroup' in flaskg.groups
+        assert u'not existing group' not in flaskg.groups
 
 
 coverage_modules = ['MoinMoin.datastruct.backends.composite_groups']
--- a/MoinMoin/datastruct/backends/_tests/test_config_dicts.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_config_dicts.py	Sat Aug 02 21:08:11 2014 +0200
@@ -10,14 +10,21 @@
 from MoinMoin.datastruct import ConfigDicts
 from MoinMoin._tests import wikiconfig
 
+import pytest
+
 
 class TestConfigDictsBackend(DictsBackendTest):
 
-    class Config(wikiconfig.Config):
+    @pytest.fixture
+    def cfg(self):
 
-        def dicts(self):
-            dicts = DictsBackendTest.dicts
-            return ConfigDicts(dicts)
+        class Config(wikiconfig.Config):
+
+            def dicts(self):
+                dicts = DictsBackendTest.dicts
+                return ConfigDicts(dicts)
+
+        return Config
 
     def test__iter__(self):
         ConfigDicts_obj = ConfigDicts(DictsBackendTest.dicts)
--- a/MoinMoin/datastruct/backends/_tests/test_config_groups.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_config_groups.py	Sat Aug 02 21:08:11 2014 +0200
@@ -10,14 +10,19 @@
 from MoinMoin.datastruct import ConfigGroups
 from MoinMoin._tests import wikiconfig
 
+import pytest
+
 
 class TestConfigGroupsBackend(GroupsBackendTest):
-
-    class Config(wikiconfig.Config):
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
 
-        def groups(self):
-            groups = GroupsBackendTest.test_groups
-            return ConfigGroups(groups)
+            def groups(self):
+                groups = GroupsBackendTest.test_groups
+                return ConfigGroups(groups)
+
+        return Config
 
 
 coverage_modules = ['MoinMoin.datastruct.backends.config_groups']
--- a/MoinMoin/datastruct/backends/_tests/test_lazy_config_groups.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_lazy_config_groups.py	Sat Aug 02 21:08:11 2014 +0200
@@ -11,6 +11,8 @@
 from MoinMoin.datastruct import ConfigGroups, CompositeGroups
 from MoinMoin._tests import wikiconfig
 
+import pytest
+
 
 class TestLazyConfigGroups(GroupsBackendTest):
 
@@ -21,11 +23,15 @@
 
     expanded_groups = test_groups
 
-    class Config(wikiconfig.Config):
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
 
-        def groups(self):
-            groups = TestLazyConfigGroups.test_groups
-            return ConfigLazyGroups(groups)
+            def groups(self):
+                groups = TestLazyConfigGroups.test_groups
+                return ConfigLazyGroups(groups)
+
+        return Config
 
     def test_contains_group(self):
         """
@@ -36,22 +42,24 @@
 
 
 class TestCompositeAndLazyConfigGroups(GroupsBackendTest):
-
-    class Config(wikiconfig.Config):
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
 
-        def groups(self):
-            config_groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor1', u'John'],
-                             u'RecursiveGroup': [u'Something', u'OtherRecursiveGroup'],
-                             u'OtherRecursiveGroup': [u'RecursiveGroup', u'Anything', u'NotExistingGroup'],
-                             u'ThirdRecursiveGroup': [u'ThirdRecursiveGroup', u'Banana'],
-                             u'CheckNotExistingGroup': [u'NotExistingGroup']}
+            def groups(self):
+                config_groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor1', u'John'],
+                                 u'RecursiveGroup': [u'Something', u'OtherRecursiveGroup'],
+                                 u'OtherRecursiveGroup': [u'RecursiveGroup', u'Anything', u'NotExistingGroup'],
+                                 u'ThirdRecursiveGroup': [u'ThirdRecursiveGroup', u'Banana'],
+                                 u'CheckNotExistingGroup': [u'NotExistingGroup']}
 
-            lazy_groups = {u'AdminGroup': [u'Admin1', u'Admin2', u'John'],
-                           u'OtherGroup': [u'SomethingOther'],
-                           u'EmptyGroup': []}
+                lazy_groups = {u'AdminGroup': [u'Admin1', u'Admin2', u'John'],
+                               u'OtherGroup': [u'SomethingOther'],
+                               u'EmptyGroup': []}
 
-            return CompositeGroups(ConfigGroups(config_groups),
-                                   ConfigLazyGroups(lazy_groups))
+                return CompositeGroups(ConfigGroups(config_groups),
+                                       ConfigLazyGroups(lazy_groups))
 
+        return Config
 
 coverage_modules = ['MoinMoin.datastruct.backends.config_lazy_groups']
--- a/MoinMoin/datastruct/backends/_tests/test_wiki_dicts.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_wiki_dicts.py	Sat Aug 02 21:08:11 2014 +0200
@@ -14,6 +14,9 @@
 from MoinMoin.constants.keys import SOMEDICT
 from MoinMoin._tests import become_trusted, update_item
 
+import pytest
+
+
 DATA = "This is a dict item."
 
 
@@ -22,7 +25,8 @@
     # Suppose that default configuration for the dicts is used which
     # is WikiDicts backend.
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         become_trusted()
 
         somedict = {u"First": u"first item",
--- a/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Sat Aug 02 21:08:11 2014 +0200
@@ -19,9 +19,11 @@
 from MoinMoin.datastruct import GroupDoesNotExistError
 from MoinMoin.constants.keys import NAME, USERGROUP
 from MoinMoin.security import AccessControlList
-from MoinMoin.user import User
 from MoinMoin._tests import become_trusted, create_random_string_list, update_item
 
+import pytest
+
+
 DATA = "This is a group item"
 
 
@@ -30,7 +32,8 @@
     # Suppose that default configuration for the groups is used which
     # is WikiGroups backend.
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         become_trusted()
         for group, members in self.test_groups.iteritems():
             update_item(group, {USERGROUP: members}, DATA)
@@ -40,11 +43,11 @@
         Tests renaming of a group item.
         """
         become_trusted()
-        item = update_item(u'SomeGroup', {USERGROUP: ["ExampleUser"]}, DATA)
+        update_item(u'SomeGroup', {USERGROUP: ["ExampleUser"]}, DATA)
         assert u'ExampleUser' in flaskg.groups[u'SomeGroup']
         pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'AnotherGroup'])
 
-        item = update_item(u'SomeGroup', {NAME: [u'AnotherGroup', ], USERGROUP: ["ExampleUser"]}, DATA)
+        update_item(u'SomeGroup', {NAME: [u'AnotherGroup', ], USERGROUP: ["ExampleUser"]}, DATA)
         assert u'ExampleUser' in flaskg.groups[u'AnotherGroup']
         pytest.raises(GroupDoesNotExistError, lambda: flaskg.groups[u'SomeGroup'])
 
--- a/MoinMoin/items/_tests/test_Blog.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/items/_tests/test_Blog.py	Sat Aug 02 21:08:11 2014 +0200
@@ -10,7 +10,7 @@
 from datetime import datetime
 from flask import url_for
 
-from MoinMoin._tests import become_trusted, update_item
+from MoinMoin._tests import update_item
 from MoinMoin.items import Item
 from MoinMoin.constants.keys import CONTENTTYPE, ITEMTYPE, PTIME, ACL, TAGS
 from MoinMoin.constants.misc import ANON
@@ -18,8 +18,14 @@
 from MoinMoin.items.blog import Blog, BlogEntry
 from MoinMoin.themes import utctimestamp
 
+import pytest
+
 
 class TestView(object):
+    @pytest.fixture(autouse=True)
+    def set_self_app(self, app):
+        self.app = app
+
     def _test_view(self, item_name, req_args={}, data_tokens=[], exclude_data_tokens=[], regex=None):
         with self.app.test_client() as c:
             rv = c.get(url_for('frontend.show_item', item_name=item_name, **req_args))
@@ -110,12 +116,13 @@
         item = Item.create(self.name, itemtype=ITEMTYPE_BLOG)
         item._save(self.meta, self.data, comment=self.comment)
         # publish some entries with tags
-        entries_meta = [{ITEMTYPE: ITEMTYPE_BLOG_ENTRY, PTIME: 1000, TAGS: [u'foo', u'bar', u'moin', ]},
-                        {ITEMTYPE: ITEMTYPE_BLOG_ENTRY, PTIME: 3000, TAGS: [u'foo', u'bar', u'baz', ]},
-                        {ITEMTYPE: ITEMTYPE_BLOG_ENTRY, PTIME: 2000, TAGS: [u'baz', u'moin', ]}, ]
-        for i in xrange(len(entries_meta)):
-            entry = self.entries[i]
-            entry_meta = entries_meta[i]
+        entries_meta = [
+            {PTIME: 1000, TAGS: [u'foo', u'bar', u'moin']},
+            {PTIME: 3000, TAGS: [u'foo', u'bar', u'baz']},
+            {PTIME: 2000, TAGS: [u'baz', u'moin']},
+        ]
+        for entry, entry_meta in zip(self.entries, entries_meta):
+            entry_meta.update(self.entry_meta)
             item = Item.create(entry['name'], itemtype=ITEMTYPE_BLOG_ENTRY)
             item._save(entry_meta, entry['data'], comment=self.comment)
         # filter by non-existent tag 'non-existent'
--- a/MoinMoin/macro/_tests/test_GetVal.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/macro/_tests/test_GetVal.py	Sat Aug 02 21:08:11 2014 +0200
@@ -11,29 +11,21 @@
 from MoinMoin.macro.GetVal import *
 from MoinMoin.constants.keys import SOMEDICT
 from MoinMoin._tests import become_trusted, update_item
-from MoinMoin.conftest import init_test_app, deinit_test_app
-from MoinMoin._tests import wikiconfig
-
-DATA = "This is a dict item."
 
 
 class TestMacro(object):
-    """ Test: GetVal.Macro """
-
-    def setup_method(self, method):
-        # temporary hack till we apply test cleanup mechanism on tests.
-        self.app, self.ctx = init_test_app(wikiconfig.Config)
+    @pytest.fixture
+    def test_dict(self):
         become_trusted()
         somedict = {u"One": u"1",
                     u"Two": u"2"}
-        update_item(u'TestDict', {SOMEDICT: somedict}, DATA)
+        update_item(u'TestDict', {SOMEDICT: somedict}, "This is a dict item.")
 
-    def teardown_method(self, method):
-        deinit_test_app(self.app, self.ctx)
+        return u"TestDict"
 
-    def test_Macro(self):
+    def test_Macro(self, test_dict):
         macro_obj = Macro()
-        arguments = [u'TestDict']
+        arguments = [test_dict]
         with pytest.raises(ValueError):
             macro_obj.macro('content', arguments, 'page_url', 'alternative')
 
--- a/MoinMoin/security/_tests/test_security.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/security/_tests/test_security.py	Sat Aug 02 21:08:11 2014 +0200
@@ -18,8 +18,7 @@
 from MoinMoin.constants.keys import NAME, ACL
 from MoinMoin.datastruct import ConfigGroups
 
-from MoinMoin._tests import update_item
-from MoinMoin._tests import become_trusted
+from MoinMoin._tests import update_item, become_trusted, wikiconfig
 
 
 def acliter(acl):
@@ -266,20 +265,22 @@
 
 class TestGroupACL(object):
 
-    from MoinMoin._tests import wikiconfig
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            def groups(cfg):
+                groups = {
+                    u'PGroup': frozenset([u'Antony', u'Beatrice', ]),
+                    u'AGroup': frozenset([u'All', ]),
+                    # note: the next line is a INTENDED misnomer, there is "All" in
+                    # the group NAME, but not in the group members. This makes
+                    # sure that a bug that erroneously checked "in groupname" (instead
+                    # of "in groupmembers") does not reappear.
+                    u'AllGroup': frozenset([]),  # note: intended misnomer
+                }
+                return ConfigGroups(groups)
 
-    class Config(wikiconfig.Config):
-        def groups(cfg):
-            groups = {
-                u'PGroup': frozenset([u'Antony', u'Beatrice', ]),
-                u'AGroup': frozenset([u'All', ]),
-                # note: the next line is a INTENDED misnomer, there is "All" in
-                # the group NAME, but not in the group members. This makes
-                # sure that a bug that erroneously checked "in groupname" (instead
-                # of "in groupmembers") does not reappear.
-                u'AllGroup': frozenset([]),  # note: intended misnomer
-            }
-            return ConfigGroups(groups)
+        return Config
 
     def testApplyACLByGroup(self):
         """ security: applying acl by group name"""
@@ -333,13 +334,16 @@
         (subitem_4boss, u'JoeDoe:read,write', u'Only JoeDoe (the boss) may write'),
     ]
 
-    from MoinMoin._tests import wikiconfig
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            default_acl = dict(hierarchic=False, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"All:read,write", after=u"All:read")
+            acl_functions = u"SuperUser:superuser NoTextchaUser:notextcha"
 
-    class Config(wikiconfig.Config):
-        default_acl = dict(hierarchic=False, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"All:read,write", after=u"All:read")
-        acl_functions = u"SuperUser:superuser NoTextchaUser:notextcha"
+        return Config
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         become_trusted(username=u'WikiAdmin')
         for item_name, item_acl, item_content in self.items:
             if item_acl is not None:
@@ -347,7 +351,7 @@
             else:
                 update_item(item_name, {}, item_content)
 
-    def testItemACLs(self):
+    def test_ItemACLs(self):
         """ security: test item acls """
         tests = [
             # itemname, username, expected_rights
@@ -369,24 +373,17 @@
             u = User(auth_username=username)
             u.valid = True
 
-            def _have_right(u, right, itemname):
+            # User should have these rights...
+            for right in may:
                 can_access = getattr(u.may, right)(itemname)
                 assert can_access, "{0!r} may {1} {2!r} (normal)".format(u.name, right, itemname)
 
-            # User should have these rights...
-            for right in may:
-                yield _have_right, u, right, itemname
-
-            def _not_have_right(u, right, itemname):
+            # User should NOT have these rights:
+            mayNot = [right for right in app.cfg.acl_rights_contents if right not in may]
+            for right in mayNot:
                 can_access = getattr(u.may, right)(itemname)
                 assert not can_access, "{0!r} may not {1} {2!r} (normal)".format(u.name, right, itemname)
 
-            # User should NOT have these rights:
-            mayNot = [right for right in app.cfg.acl_rights_contents
-                      if right not in may]
-            for right in mayNot:
-                yield _not_have_right, u, right, itemname
-
         # check function rights
         u = User(auth_username='SuperUser')
         assert u.may.superuser()
@@ -420,12 +417,15 @@
         (subitem_4boss, u'JoeDoe:read,write', u'Only JoeDoe (the boss) may write'),
     ]
 
-    from MoinMoin._tests import wikiconfig
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            default_acl = dict(hierarchic=True, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"All:read,write", after=u"All:read")
 
-    class Config(wikiconfig.Config):
-        default_acl = dict(hierarchic=True, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"All:read,write", after=u"All:read")
+        return Config
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         become_trusted(username=u'WikiAdmin')
         for item_name, item_acl, item_content in self.items:
             if item_acl is not None:
@@ -457,23 +457,17 @@
             u = User(auth_username=username)
             u.valid = True
 
-            def _have_right(u, right, itemname):
+            # User should have these rights...
+            for right in may:
                 can_access = getattr(u.may, right)(itemname)
                 assert can_access, "{0!r} may {1} {2!r} (hierarchic)".format(u.name, right, itemname)
 
-            # User should have these rights...
-            for right in may:
-                yield _have_right, u, right, itemname
-
-            def _not_have_right(u, right, itemname):
-                can_access = getattr(u.may, right)(itemname)
-                assert not can_access, "{0!r} may not {1} {2!r} (hierarchic)".format(u.name, right, itemname)
-
             # User should NOT have these rights:
             mayNot = [right for right in app.cfg.acl_rights_contents
                       if right not in may]
             for right in mayNot:
-                yield _not_have_right, u, right, itemname
+                can_access = getattr(u.may, right)(itemname)
+                assert not can_access, "{0!r} may not {1} {2!r} (hierarchic)".format(u.name, right, itemname)
 
 
 class TestItemHierachicalAclsMultiItemNames(object):
@@ -494,12 +488,14 @@
         (c12, None, c12),  # no own acl -> inherit from parents
     ]
 
-    from MoinMoin._tests import wikiconfig
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            default_acl = dict(hierarchic=True, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"Editor:read,write", after=u"All:read")
+        return Config
 
-    class Config(wikiconfig.Config):
-        default_acl = dict(hierarchic=True, before=u"WikiAdmin:admin,read,write,create,destroy", default=u"Editor:read,write", after=u"All:read")
-
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         become_trusted(username=u'WikiAdmin')
         for item_names, item_acl, item_content in self.items:
             meta = {NAME: item_names}
@@ -534,23 +530,17 @@
             u.valid = True
             itemname = itemnames[0]
 
-            def _have_right(u, right, itemname):
+            # User should have these rights...
+            for right in may:
                 can_access = getattr(u.may, right)(itemname)
                 assert can_access, "{0!r} may {1} {2!r} (hierarchic)".format(u.name, right, itemname)
 
-            # User should have these rights...
-            for right in may:
-                yield _have_right, u, right, itemname
-
-            def _not_have_right(u, right, itemname):
-                can_access = getattr(u.may, right)(itemname)
-                assert not can_access, "{0!r} may not {1} {2!r} (hierarchic)".format(u.name, right, itemname)
-
             # User should NOT have these rights:
             mayNot = [right for right in app.cfg.acl_rights_contents
                       if right not in may]
             for right in mayNot:
-                yield _not_have_right, u, right, itemname
+                can_access = getattr(u.may, right)(itemname)
+                assert not can_access, "{0!r} may not {1} {2!r} (hierarchic)".format(u.name, right, itemname)
 
 
 # XXX TODO add tests for a user having multiple usernames (one resulting in more permissions than other)
--- a/MoinMoin/security/_tests/test_textcha.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/security/_tests/test_textcha.py	Sat Aug 02 21:08:11 2014 +0200
@@ -11,10 +11,13 @@
 from MoinMoin.security.textcha import TextCha, TextChaValid, TextChaizedForm
 from MoinMoin.constants.keys import LOCALE
 
+import pytest
+
 
 class TestTextCha(object):
     """ Test: class TextCha """
-    def setup_method(self, method):
+    @pytest.yield_fixture(autouse=True)
+    def custom_setup(self):
         cfg = app.cfg
         cfg.textchas = {
             'test_user_locale': {
@@ -25,8 +28,8 @@
         cfg.secrets['security/textcha'] = "test_secret"
         flaskg.user.profile[LOCALE] = 'test_user_locale'
 
-    def teardown_method(self, method):
-        cfg = app.cfg
+        yield
+
         cfg.textchas = None
         cfg.secrets.pop('security/textcha')
         flaskg.user.profile[LOCALE] = None
@@ -92,7 +95,8 @@
 
 class TestTextChaValid(object):
     """ Test: class TextChaValid """
-    def setup_method(self, method):
+    @pytest.yield_fixture(autouse=True)
+    def custom_setup(self):
         cfg = app.cfg
         cfg.textchas = {
             'test_user_locale': {'Good Question': 'Good Answer'}
@@ -100,8 +104,8 @@
         cfg.secrets['security/textcha'] = "test_secret"
         flaskg.user.profile[LOCALE] = 'test_user_locale'
 
-    def teardown_method(self, method):
-        cfg = app.cfg
+        yield
+
         cfg.textchas = None
         cfg.secrets.pop('security/textcha')
         flaskg.user.profile[LOCALE] = None
--- a/MoinMoin/storage/middleware/_tests/test_indexing.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/storage/middleware/_tests/test_indexing.py	Sat Aug 02 21:08:11 2014 +0200
@@ -36,11 +36,10 @@
 class TestIndexingMiddleware(object):
     reinit_storage = True  # cleanup after each test method
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def imw(self):
         self.imw = flaskg.unprotected_storage
-
-    def teardown_method(self, method):
-        pass
+        return self.imw
 
     def test_nonexisting_item(self):
         item = self.imw[u'foo']
@@ -416,14 +415,15 @@
 class TestProtectedIndexingMiddleware(object):
     reinit_storage = True  # cleanup after each test method
 
-    class Config(wikiconfig.Config):
-        auth = [GivenAuth(user_name=u'joe', autocreate=True), ]
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            auth = [GivenAuth(user_name=u'joe', autocreate=True), ]
+        return Config
 
-    def setup_method(self, method):
-        self.imw = flaskg.storage
-
-    def teardown_method(self, method):
-        pass
+    @pytest.fixture(autouse=True)
+    def imw(self):
+        self.imw = flaskg.unprotected_storage
 
     def test_documents(self):
         item_name = u'public'
--- a/MoinMoin/storage/middleware/_tests/test_protecting.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/storage/middleware/_tests/test_protecting.py	Sat Aug 02 21:08:11 2014 +0200
@@ -40,13 +40,10 @@
 
 
 class TestProtectingMiddleware(TestIndexingMiddleware):
-    def setup_method(self, method):
-        super(TestProtectingMiddleware, self).setup_method(method)
-        self.imw = ProtectingMiddleware(self.imw, FakeUser(u'joe'), acl_mapping=acl_mapping)
-
-    def teardown_method(self, method):
-        self.imw = self.imw.indexer
-        super(TestProtectingMiddleware, self).teardown_method(method)
+    @pytest.fixture(autouse=True)
+    def protected_imw(self, imw):
+        self.imw = ProtectingMiddleware(imw, FakeUser(u'joe'), acl_mapping=acl_mapping)
+        return self.imw
 
     def _dummy(self):
         # replacement for tests that use unsupported methods / attributes
--- a/MoinMoin/themes/_tests/test_navi_bar.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/themes/_tests/test_navi_bar.py	Sat Aug 02 21:08:11 2014 +0200
@@ -13,15 +13,22 @@
 from MoinMoin.themes import ThemeSupport
 from MoinMoin import themes
 
+import pytest
+
 
 class TestNaviBar(object):
-    class Config(wikiconfig.Config):
-        interwiki_map = dict(Self='http://localhost:8080/', MoinMoin='http://moinmo.in/', )
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            interwiki_map = dict(Self='http://localhost:8080/', MoinMoin='http://moinmo.in/', )
 
-    def setup_method(self, method):
-        self.theme = ThemeSupport(app.cfg)
+        return Config
 
-    def test_split_navilink(self):
+    @pytest.fixture
+    def theme(self):
+        return ThemeSupport(app.cfg)
+
+    def test_split_navilink(self, theme):
         tests = [
             # (navilink, (href, text, interwiki)),
             ('ItemName', ('/ItemName', 'ItemName', '')),
@@ -33,11 +40,11 @@
             ('[[http://example.org/|LinkText]]', ('http://example.org/', 'LinkText', '')),
         ]
         for navilink, expected in tests:
-            result = self.theme.split_navilink(navilink)
+            result = theme.split_navilink(navilink)
             assert result == expected
 
-    def test_location_breadcrumbs(self):
-        test_result = ThemeSupport.location_breadcrumbs(self.theme, 'some/place/test_item')
+    def test_location_breadcrumbs(self, theme):
+        test_result = ThemeSupport.location_breadcrumbs(theme, 'some/place/test_item')
         test_segment_name_1, test_item_name_1, test_item_exists_1 = test_result[0]
         test_segment_name_2, test_item_name_2, test_item_exists_2 = test_result[1]
         test_segment_name_3, test_item_name_3, test_item_exists_3 = test_result[2]
@@ -52,8 +59,8 @@
         assert test_segment_name_4.value == 'test_item'
         assert test_item_name_4.value == 'some/place/test_item'
 
-    def test_parent_item(self):
-        test_result = ThemeSupport.parent_item(self.theme, 'moin/moin-2.0/Item')
+    def test_parent_item(self, theme):
+        test_result = ThemeSupport.parent_item(theme, 'moin/moin-2.0/Item')
         expected = 'moin/moin-2.0'
         assert test_result == expected, 'Expected "%(expected)s" but got "%(test_result)s"' % locals()
 
--- a/MoinMoin/util/_tests/test_interwiki.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/util/_tests/test_interwiki.py	Sat Aug 02 21:08:11 2014 +0200
@@ -23,13 +23,18 @@
 
 
 class TestInterWiki(object):
-    class Config(wikiconfig.Config):
-        interwiki_map = {'Self': 'http://localhost:8080/',
-                         'MoinMoin': 'http://moinmo.in/',
-                         'OtherWiki': 'http://otherwiki.com/',
-                         'OtherWiki/ns1': 'http://otherwiki.com/ns1/',
-                         'OtherWiki/ns1/ns2': 'http://otherwiki.com/ns1/ns2/',
-        }
+    @pytest.fixture
+    def cfg(self):
+        class Config(wikiconfig.Config):
+            interwiki_map = {
+                'Self': 'http://localhost:8080/',
+                'MoinMoin': 'http://moinmo.in/',
+                'OtherWiki': 'http://otherwiki.com/',
+                'OtherWiki/ns1': 'http://otherwiki.com/ns1/',
+                'OtherWiki/ns1/ns2': 'http://otherwiki.com/ns1/ns2/',
+            }
+
+        return Config
 
     def test_url_for_item(self):
         before_wiki()
--- a/MoinMoin/util/_tests/test_notifications.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/util/_tests/test_notifications.py	Sat Aug 02 21:08:11 2014 +0200
@@ -17,11 +17,14 @@
 from MoinMoin.util.notifications import Notification, get_item_last_revisions, DESTROY_REV, DESTROY_ALL
 from MoinMoin.util.interwiki import split_fqname
 
+import pytest
+
 
 class TestNotifications(object):
     reinit_storage = True
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         self.imw = flaskg.unprotected_storage
         self.item_name = u"foo"
         self.fqname = split_fqname(self.item_name)
--- a/MoinMoin/util/_tests/test_pysupport.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/util/_tests/test_pysupport.py	Sat Aug 02 21:08:11 2014 +0200
@@ -45,7 +45,8 @@
 
     name = 'Parser'
 
-    def setup_method(self, method):
+    @pytest.fixture(autouse=True)
+    def custom_setup(self):
         """ Check for valid plugin package """
         self.pluginDirectory = os.path.join(app.cfg.data_dir, 'plugin', 'parser')
         self.checkPackage(self.pluginDirectory)
@@ -130,4 +131,3 @@
 
 
 coverage_modules = ['MoinMoin.util.pysupport']
-pytest.main("-x test_pysupport.py")
--- a/MoinMoin/util/_tests/test_subscriptions.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/MoinMoin/util/_tests/test_subscriptions.py	Sat Aug 02 21:08:11 2014 +0200
@@ -18,18 +18,30 @@
 class TestSubscriptions(object):
     reinit_storage = True
 
-    def setup_method(self, method):
-        # create an item
-        self.item_name = u'foo'
-        self.tagname = u'XXX'
-        self.namespace = NAMESPACE_DEFAULT
-        meta = {CONTENTTYPE: u'text/plain;charset=utf-8', TAGS: [self.tagname]}
-        item = Item.create(self.item_name)
+    @pytest.fixture
+    def item_name(self):
+        return u'foo'
+
+    @pytest.fixture
+    def tag_name(self):
+        return u'XXX'
+
+    @pytest.fixture
+    def namespace(self):
+        return NAMESPACE_DEFAULT
+
+    @pytest.fixture
+    def meta(self, tag_name):
+        return {CONTENTTYPE: u'text/plain;charset=utf-8', TAGS: [tag_name]}
+
+    @pytest.fixture
+    def item(self, item_name, meta):
+        item = Item.create(item_name)
         item._save(meta)
-        self.item = Item.create(self.item_name)
+        return Item.create(item_name)
 
-    def test_get_subscribers(self):
-        users = get_subscribers(**self.item.meta)
+    def test_get_subscribers(self, item, item_name, namespace, tag_name):
+        users = get_subscribers(**item.meta)
         assert users == set()
 
         name1 = u'baz'
@@ -45,17 +57,17 @@
         user2 = user.User(name=name2, password=password)
         user.create_user(username=name3, password=password, email=email3, locale=u"en")
         user3 = user.User(name=name3, password=password, email1=email3)
-        subscribers = get_subscribers(**self.item.meta)
+        subscribers = get_subscribers(**item.meta)
         assert subscribers == set()
 
         namere = r'.*'
         nameprefix = u"fo"
         subscription_lists = [
-            ["{0}:{1}".format(ITEMID, self.item.meta[ITEMID])],
-            ["{0}:{1}:{2}".format(TAGS, self.namespace, self.tagname)],
-            ["{0}:{1}:{2}".format(NAME, self.namespace, self.item_name)],
-            ["{0}:{1}:{2}".format(NAMERE, self.namespace, namere)],
-            ["{0}:{1}:{2}".format(NAMEPREFIX, self.namespace, nameprefix)],
+            ["{0}:{1}".format(ITEMID, item.meta[ITEMID])],
+            ["{0}:{1}:{2}".format(TAGS, namespace, tag_name)],
+            ["{0}:{1}:{2}".format(NAME, namespace, item_name)],
+            ["{0}:{1}:{2}".format(NAMERE, namespace, namere)],
+            ["{0}:{1}:{2}".format(NAMEPREFIX, namespace, nameprefix)],
         ]
         users = [user1, user2, user3]
         expected_names = {user1.name0, user2.name0}
@@ -63,32 +75,32 @@
             for user_ in users:
                 user_.profile._meta[SUBSCRIPTIONS] = subscriptions
                 user_.save(force=True)
-            subscribers = get_subscribers(**self.item.meta)
+            subscribers = get_subscribers(**item.meta)
             subscribers_names = {subscriber.name for subscriber in subscribers}
             assert subscribers_names == expected_names
 
         meta = {CONTENTTYPE: u'text/plain;charset=utf-8',
                 ACL: u"{0}: All:read,write".format(user1.name0)}
-        self.item._save(meta, comment=u"")
-        self.item = Item.create(self.item_name)
-        subscribers = get_subscribers(**self.item.meta)
+        item._save(meta, comment=u"")
+        item = Item.create(item_name)
+        subscribers = get_subscribers(**item.meta)
         assert {subscriber.name for subscriber in subscribers} == {user2.name0}
 
-    def test_get_matched_subscription_patterns(self):
-        meta = self.item.meta
+    def test_get_matched_subscription_patterns(self, item, namespace):
+        meta = item.meta
         patterns = get_matched_subscription_patterns([], **meta)
         assert patterns == []
         non_matching_patterns = [
             "{0}:{1}:{2}".format(NAMERE, NAMESPACE_USERPROFILES, ".*"),
-            "{0}:{1}:{2}".format(NAMERE, self.namespace, "\d+"),
-            "{0}:{1}:{2}".format(NAMEPREFIX, self.namespace, "bar"),
+            "{0}:{1}:{2}".format(NAMERE, namespace, "\d+"),
+            "{0}:{1}:{2}".format(NAMEPREFIX, namespace, "bar"),
         ]
         patterns = get_matched_subscription_patterns(non_matching_patterns, **meta)
         assert patterns == []
 
         matching_patterns = [
-            "{0}:{1}:{2}".format(NAMERE, self.namespace, "fo+"),
-            "{0}:{1}:{2}".format(NAMEPREFIX, self.namespace, "fo"),
+            "{0}:{1}:{2}".format(NAMERE, namespace, "fo+"),
+            "{0}:{1}:{2}".format(NAMEPREFIX, namespace, "fo"),
         ]
         patterns = get_matched_subscription_patterns(non_matching_patterns + matching_patterns, **meta)
         assert patterns == matching_patterns
--- a/setup.py	Sat Jul 19 17:22:05 2014 +0200
+++ b/setup.py	Sat Aug 02 21:08:11 2014 +0200
@@ -93,11 +93,8 @@
         'Jinja2>=2.7',  # template engine
         'pygments>=1.4',  # src code / text file highlighting
         'Werkzeug>=0.9',  # wsgi toolkit
-        'pytest>=2.1, <2.3',  # pytest is needed by unit tests
-                              # note: currently 2.3.x is not compatible with our test code,
-                              # likely due to the fixtures changes.
-        'pytest-pep8<1.0.3',  # coding style checker
-                              # note: pytest-pep8 1.0.3 needs pytest 2.3
+        'pytest<2.7',  # pytest is needed by unit tests (only tested with 2.5 and 2.6)
+        'pytest-pep8<1.1',  # coding style checker (only tested with 1.0.x)
         'whoosh>=2.5.0',  # needed for indexed search
         'sphinx>=1.1',  # needed to build the docs
         'pdfminer==20110515',  # pdf -> text/plain conversion, XXX 20131113 fails see #385