You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by di...@apache.org on 2022/09/21 16:04:47 UTC

[allura] 02/03: [#8455] converted the remaining modules fully to pytest

This is an automated email from the ASF dual-hosted git repository.

dill0wn pushed a commit to branch dw/8455-part2
in repository https://gitbox.apache.org/repos/asf/allura.git

commit dfb1887fc1ea0288f7b799a8f21c78705e85e244
Author: Dillon Walls <di...@slashdotmedia.com>
AuthorDate: Fri Sep 16 18:46:46 2022 +0000

    [#8455] converted the remaining modules fully to pytest
---
 Allura/allura/tests/functional/test_discuss.py     |  2 +-
 Allura/allura/tests/model/test_artifact.py         |  5 ++--
 Allura/allura/tests/test_app.py                    |  6 ++--
 Allura/allura/tests/test_commands.py               | 33 +++++++++++-----------
 Allura/allura/tests/test_helpers.py                | 11 ++++----
 Allura/allura/tests/test_mail_util.py              | 26 ++++++++---------
 Allura/allura/tests/test_multifactor.py            | 24 ++++++++--------
 Allura/allura/tests/test_patches.py                | 13 ++++-----
 Allura/allura/tests/test_plugin.py                 | 22 +++++----------
 Allura/allura/tests/test_security.py               |  4 +--
 Allura/allura/tests/test_utils.py                  | 13 +++------
 Allura/allura/tests/test_webhooks.py               | 15 ++++------
 Allura/allura/tests/unit/spam/test_akismet.py      |  2 +-
 .../tests/unit/test_helpers/test_set_context.py    |  8 +++---
 .../allura/tests/unit/test_package_path_loader.py  |  8 +++---
 .../forgeactivity/tests/functional/test_rest.py    |  8 +++---
 .../forgeactivity/tests/functional/test_root.py    |  8 +++---
 ForgeBlog/forgeblog/tests/functional/test_rest.py  |  4 +--
 ForgeBlog/forgeblog/tests/test_app.py              |  4 +--
 ForgeBlog/forgeblog/tests/test_commands.py         |  2 +-
 ForgeBlog/forgeblog/tests/test_roles.py            |  2 +-
 ForgeBlog/forgeblog/tests/unit/__init__.py         |  4 +--
 .../forgediscussion/tests/functional/test_forum.py | 16 +++++------
 .../tests/functional/test_forum_admin.py           |  4 +--
 .../tests/functional/test_import.py                |  4 +--
 .../forgediscussion/tests/functional/test_rest.py  |  6 ++--
 ForgeDiscussion/forgediscussion/tests/test_app.py  |  2 +-
 .../forgediscussion/tests/test_forum_roles.py      |  2 +-
 .../forgefeedback/tests/functional/test_root.py    |  6 ----
 .../forgefeedback/tests/test_feedback_roles.py     |  2 +-
 ForgeFeedback/forgefeedback/tests/unit/__init__.py |  4 +--
 .../tests/unit/test_root_controller.py             |  4 +--
 .../forgefiles/tests/functional/test_root.py       |  3 --
 ForgeFiles/forgefiles/tests/model/__init__.py      |  4 +--
 ForgeFiles/forgefiles/tests/test_files_roles.py    |  2 +-
 .../forgegit/tests/functional/test_controllers.py  | 32 ++++++++++-----------
 ForgeGit/forgegit/tests/model/test_repository.py   | 12 ++++----
 ForgeGit/forgegit/tests/test_git_app.py            |  2 +-
 ForgeGit/forgegit/tests/test_tasks.py              |  6 ++--
 .../forgeimporters/github/tests/test_code.py       |  2 +-
 .../forgeimporters/github/tests/test_oauth.py      |  4 +--
 .../forgeimporters/github/tests/test_utils.py      |  2 +-
 .../forgeimporters/github/tests/test_wiki.py       |  2 +-
 .../forgeimporters/tests/forge/test_discussion.py  | 11 +++-----
 .../forgeimporters/tests/forge/test_tracker.py     | 10 +++----
 .../tests/github/functional/test_github.py         |  4 +--
 .../forgeimporters/tests/github/test_extractor.py  |  2 +-
 .../forgeimporters/tests/github/test_tracker.py    |  6 ++--
 ForgeImporters/forgeimporters/tests/test_base.py   | 10 +++----
 .../forgeimporters/trac/tests/test_tickets.py      |  6 ++--
 ForgeLink/forgelink/tests/functional/test_rest.py  |  8 +++---
 ForgeLink/forgelink/tests/test_app.py              |  2 +-
 .../forgesvn/tests/functional/test_controllers.py  |  8 +++---
 ForgeSVN/forgesvn/tests/model/test_repository.py   | 24 ++++++++--------
 .../forgesvn/tests/model/test_svnimplementation.py |  2 +-
 ForgeSVN/forgesvn/tests/test_svn_app.py            |  2 +-
 ForgeSVN/forgesvn/tests/test_tasks.py              |  4 +--
 .../forgeshorturl/tests/functional/test.py         |  4 +--
 .../tests/command/test_fix_discussion.py           |  2 +-
 .../forgetracker/tests/functional/test_rest.py     | 16 +++++------
 .../forgetracker/tests/functional/test_root.py     | 24 ++++++----------
 ForgeTracker/forgetracker/tests/test_app.py        |  2 +-
 .../forgetracker/tests/test_tracker_roles.py       |  2 +-
 ForgeTracker/forgetracker/tests/unit/__init__.py   |  4 +--
 .../forgetracker/tests/unit/test_globals_model.py  |  4 +--
 .../tests/unit/test_root_controller.py             | 12 ++++----
 .../forgetracker/tests/unit/test_ticket_model.py   | 12 ++------
 ForgeUserStats/forgeuserstats/tests/test_model.py  |  2 +-
 ForgeUserStats/forgeuserstats/tests/test_stats.py  |  8 +++---
 ForgeWiki/forgewiki/tests/functional/test_rest.py  |  8 +++---
 ForgeWiki/forgewiki/tests/functional/test_root.py  |  4 +--
 ForgeWiki/forgewiki/tests/test_app.py              |  4 +--
 ForgeWiki/forgewiki/tests/test_wiki_roles.py       |  2 +-
 scripts/perf/call_count.py                         |  4 +--
 74 files changed, 254 insertions(+), 304 deletions(-)

diff --git a/Allura/allura/tests/functional/test_discuss.py b/Allura/allura/tests/functional/test_discuss.py
index 8adb1a1af..3c0ed26ef 100644
--- a/Allura/allura/tests/functional/test_discuss.py
+++ b/Allura/allura/tests/functional/test_discuss.py
@@ -17,7 +17,7 @@
 
 import os
 from mock import patch
-from alluratest.tools import assert_in, assert_not_in, assert_equal, assert_false, assert_true, assert_raises
+import pytest
 from webtest.app import AppError
 from ming.odm import session
 
diff --git a/Allura/allura/tests/model/test_artifact.py b/Allura/allura/tests/model/test_artifact.py
index 8f0100e82..25863cc6f 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -22,8 +22,9 @@ import re
 from datetime import datetime
 
 from tg import tmpl_context as c
-from alluratest.tools import with_setup, assert_raises, assert_equal
+from alluratest.tools import with_setup
 from mock import patch
+import pytest
 from ming.orm.ormsession import ThreadLocalORMSession
 from ming.orm import Mapper
 from bson import ObjectId
@@ -182,7 +183,7 @@ def test_versioning():
     assert ss.shorthand_id() == pg.shorthand_id() + '#2'
     assert ss.title == pg.title
     assert ss.text == pg.text
-    assert_raises(IndexError, pg.get_version, 42)
+    pytest.raises(IndexError, pg.get_version, 42)
     pg.revert(1)
     pg.commit()
     ThreadLocalORMSession.flush_all()
diff --git a/Allura/allura/tests/test_app.py b/Allura/allura/tests/test_app.py
index 024c12fd3..71733a2d5 100644
--- a/Allura/allura/tests/test_app.py
+++ b/Allura/allura/tests/test_app.py
@@ -18,7 +18,7 @@
 from tg import tmpl_context as c
 import mock
 from ming.base import Object
-from alluratest.tools import assert_raises
+import pytest
 from formencode import validators as fev
 
 from alluratest.controller import setup_unit_test
@@ -73,8 +73,8 @@ def test_config_option_with_validator():
     v = fev.NotEmpty()
     opt = app.ConfigOption('test1', str, None, validator=v)
     assert opt.validate('val') == 'val'
-    assert_raises(fev.Invalid, opt.validate, None)
-    assert_raises(fev.Invalid, opt.validate, '')
+    pytest.raises(fev.Invalid, opt.validate, None)
+    pytest.raises(fev.Invalid, opt.validate, '')
 
 
 @with_setup(setup_method)
diff --git a/Allura/allura/tests/test_commands.py b/Allura/allura/tests/test_commands.py
index f7440e3a1..fa187c1fd 100644
--- a/Allura/allura/tests/test_commands.py
+++ b/Allura/allura/tests/test_commands.py
@@ -19,7 +19,7 @@
 import datetime
 
 import six
-from alluratest.tools import assert_raises, assert_in, with_setup
+from alluratest.tools import with_setup
 from testfixtures import OutputCapture
 
 from datadiff.tools import assert_equal
@@ -27,6 +27,7 @@ from datadiff.tools import assert_equal
 from ming.base import Object
 from ming.orm import ThreadLocalORMSession
 from mock import Mock, call, patch
+import pytest
 import pymongo
 import pkg_resources
 
@@ -58,7 +59,7 @@ def test_script():
     cmd = script.ScriptCommand('script')
     cmd.run(
         [test_config, pkg_resources.resource_filename('allura', 'tests/tscript.py')])
-    assert_raises(ValueError, cmd.run,
+    pytest.raises(ValueError, cmd.run,
                   [test_config, pkg_resources.resource_filename('allura', 'tests/tscript_error.py')])
 
 
@@ -80,9 +81,9 @@ def test_set_neighborhood_max_projects():
     assert neighborhood.features['max_projects'] is None
 
     # check validation
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'max_projects', 'string'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'max_projects', '2.8'])
 
 
@@ -104,11 +105,11 @@ def test_set_neighborhood_private():
     assert not neighborhood.features['private_projects']
 
     # check validation
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'private_projects', 'string'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'private_projects', '1'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'private_projects', '2.8'])
 
 
@@ -130,11 +131,11 @@ def test_set_neighborhood_google_analytics():
     assert not neighborhood.features['google_analytics']
 
     # check validation
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'google_analytics', 'string'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'google_analytics', '1'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'google_analytics', '2.8'])
 
 
@@ -161,15 +162,15 @@ def test_set_neighborhood_css():
     assert neighborhood.features['css'] == 'custom'
 
     # check validation
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'css', 'string'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'css', '1'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'css', '2.8'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'css', 'None'])
-    assert_raises(InvalidNBFeatureValueError, cmd.run,
+    pytest.raises(InvalidNBFeatureValueError, cmd.run,
                   [test_config, str(n_id), 'css', 'True'])
 
 
@@ -363,7 +364,7 @@ class TestTaskdCleanupCommand:
         self.cmd_class._complete_suspicious_tasks = lambda x: []
 
     def teardown_method(self, method):
-        # need to clean up setUp mocking for unit tests below to work properly
+        # need to clean up setup_method mocking for unit tests below to work properly
         self.cmd_class._check_taskd_status = self.old_check_taskd_status
         self.cmd_class._check_task = self.old_check_task
         self.cmd_class._busy_tasks = self.old_busy_tasks
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 6e03cb8c5..93a2347a1 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -23,12 +23,13 @@ import time
 import PIL
 from mock import Mock, patch
 from tg import tmpl_context as c
-from alluratest.tools import assert_equals, assert_raises, module_not_available, with_setup
+from alluratest.tools import module_not_available, with_setup
 from datadiff import tools as dd
 from webob import Request
 from webob.exc import HTTPUnauthorized
 from ming.orm import ThreadLocalORMSession
 from markupsafe import Markup
+import pytest
 
 from allura import model as M
 from allura.lib import exceptions as exc
@@ -665,24 +666,24 @@ class TestRateLimit(TestCase):
 
             start_date = now - timedelta(seconds=30)
             h.rate_limit(self.key_comment, 0, start_date)
-            with assert_raises(exc.RatelimitError):
+            with pytest.raises(exc.RatelimitError):
                 h.rate_limit(self.key_comment, 1, start_date)
 
             start_date = now - timedelta(seconds=61)
             h.rate_limit(self.key_comment, 1, start_date)
             h.rate_limit(self.key_comment, 2, start_date)
-            with assert_raises(exc.RatelimitError):
+            with pytest.raises(exc.RatelimitError):
                 h.rate_limit(self.key_comment, 3, start_date)
 
             start_date = now - timedelta(seconds=86301)
             h.rate_limit(self.key_comment, 19, start_date)
-            with assert_raises(exc.RatelimitError):
+            with pytest.raises(exc.RatelimitError):
                 h.rate_limit(self.key_comment, 20, start_date)
 
             start_date = now - timedelta(seconds=86401)
             h.rate_limit(self.key_comment, 21, start_date)
             h.rate_limit(self.key_comment, 49, start_date)
-            with assert_raises(exc.RatelimitError):
+            with pytest.raises(exc.RatelimitError):
                 h.rate_limit(self.key_comment, 50, start_date)
 
 
diff --git a/Allura/allura/tests/test_mail_util.py b/Allura/allura/tests/test_mail_util.py
index 33e80bfb0..acc4c22dd 100644
--- a/Allura/allura/tests/test_mail_util.py
+++ b/Allura/allura/tests/test_mail_util.py
@@ -20,7 +20,7 @@ from six.moves.email_mime_multipart import MIMEMultipart
 from six.moves.email_mime_text import MIMEText
 
 import mock
-from alluratest.tools import raises, assert_equal, assert_false, assert_true, assert_in
+import pytest
 from ming.orm import ThreadLocalORMSession
 from tg import config as tg_config
 
@@ -39,7 +39,7 @@ from allura.lib.mail_util import (
 from allura.lib.exceptions import AddressException
 from alluratest.pytest_helpers import with_nose_compatibility
 from allura.tests import decorators as td
-import six
+
 
 config = ConfigProxy(
     common_suffix='forgemail.domain',
@@ -55,9 +55,9 @@ class TestReactor(unittest.TestCase):
         ThreadLocalORMSession.flush_all()
         ThreadLocalORMSession.close_all()
 
-    @raises(AddressException)
     def test_parse_address_bad_domain(self):
-        parse_address('foo@bar.com')
+        with pytest.raises(AddressException):
+            parse_address('foo@bar.com')
 
     @td.with_wiki
     @mock.patch.dict(tg_config, {'forgemail.domain.alternates': '.secondary.com .tertiary.com'})
@@ -65,17 +65,17 @@ class TestReactor(unittest.TestCase):
         parse_address('foo@wiki.test.p.secondary.com')
         parse_address('foo@wiki.test.p.tertiary.com')
 
-    @raises(AddressException)
     def test_parse_address_bad_project(self):
-        parse_address('foo@wiki.unicorns.p' + config.common_suffix)
+        with pytest.raises(AddressException):
+            parse_address('foo@wiki.unicorns.p' + config.common_suffix)
 
-    @raises(AddressException)
     def test_parse_address_missing_tool(self):
-        parse_address('foo@test.p' + config.common_suffix)
+        with pytest.raises(AddressException):
+            parse_address('foo@test.p' + config.common_suffix)
 
-    @raises(AddressException)
     def test_parse_address_bad_tool(self):
-        parse_address('foo@hammer.test.p' + config.common_suffix)
+        with pytest.raises(AddressException):
+            parse_address('foo@hammer.test.p' + config.common_suffix)
 
     @td.with_wiki
     def test_parse_address_good(self):
@@ -214,10 +214,10 @@ Content-Type: text/html; charset="utf-8"
 @with_nose_compatibility
 class TestHeader:
 
-    @raises(TypeError)
     def test_bytestring(self):
-        our_header = Header(b'[asdf2:wiki] Discussion for Home page')
-        assert our_header.encode() == '[asdf2:wiki] Discussion for Home page'
+        with pytest.raises(TypeError):
+            our_header = Header(b'[asdf2:wiki] Discussion for Home page')
+            assert our_header.encode() == '[asdf2:wiki] Discussion for Home page'
 
     def test_ascii(self):
         our_header = Header('[asdf2:wiki] Discussion for Home page')
diff --git a/Allura/allura/tests/test_multifactor.py b/Allura/allura/tests/test_multifactor.py
index ba7780f3b..3ce6fdb31 100644
--- a/Allura/allura/tests/test_multifactor.py
+++ b/Allura/allura/tests/test_multifactor.py
@@ -23,7 +23,7 @@ from paste.deploy.converters import asint
 import ming
 from cryptography.hazmat.primitives.twofactor import InvalidToken
 from mock import patch, Mock
-from alluratest.tools import assert_equal, assert_raises
+import pytest
 from tg import config
 
 from allura import model as M
@@ -112,11 +112,11 @@ class TestTotpService:
         srv.verify(totp, '283397', None)
 
         time.return_value = self.sample_time + 60
-        with assert_raises(InvalidToken):
+        with pytest.raises(InvalidToken):
             srv.verify(totp, '283397', None)
 
         time.return_value = self.sample_time - 30
-        with assert_raises(InvalidToken):
+        with pytest.raises(InvalidToken):
             srv.verify(totp, '283397', None)
 
     def test_get_qr_code(self):
@@ -166,12 +166,12 @@ class TestAnyTotpServiceImplementation:
         totp = srv.Totp(key=self.sample_key)
 
         # 4th attempt (good or bad) will trip over the default limit of 3 in 30s
-        with assert_raises(InvalidToken):
+        with pytest.raises(InvalidToken):
             srv.verify(totp, '34dfvdasf', user)
-        with assert_raises(InvalidToken):
+        with pytest.raises(InvalidToken):
             srv.verify(totp, '234asdfsadf', user)
         srv.verify(totp, '283397', user)
-        with assert_raises(MultifactorRateLimitError):
+        with pytest.raises(MultifactorRateLimitError):
             srv.verify(totp, '283397', user)
 
 
@@ -269,9 +269,9 @@ class TestAnyRecoveryCodeServiceImplementation:
     def test_verify_fail(self):
         recovery = self.Service()
         user = self.mock_user()
-        with assert_raises(InvalidRecoveryCode):
+        with pytest.raises(InvalidRecoveryCode):
             recovery.verify_and_remove_code(user, '11111')
-        with assert_raises(InvalidRecoveryCode):
+        with pytest.raises(InvalidRecoveryCode):
             recovery.verify_and_remove_code(user, '')
 
     def test_verify_and_remove_code(self):
@@ -296,12 +296,12 @@ class TestAnyRecoveryCodeServiceImplementation:
         recovery.replace_codes(user, codes)
 
         # 4th attempt (good or bad) will trip over the default limit of 3 in 30s
-        with assert_raises(InvalidRecoveryCode):
+        with pytest.raises(InvalidRecoveryCode):
             recovery.verify_and_remove_code(user, '13485u0233')
-        with assert_raises(InvalidRecoveryCode):
+        with pytest.raises(InvalidRecoveryCode):
             recovery.verify_and_remove_code(user, '34123rdxafs')
         recovery.verify_and_remove_code(user, '11111')
-        with assert_raises(MultifactorRateLimitError):
+        with pytest.raises(MultifactorRateLimitError):
             recovery.verify_and_remove_code(user, '22222')
 
 
@@ -345,5 +345,5 @@ class TestGoogleAuthenticatorPamFilesystemRecoveryCodeService(TestAnyRecoveryCod
         GoogleAuthenticatorPamFilesystemTotpService().set_secret_key(self.mock_user(), None)
 
         # then it errors because no .google-authenticator file
-        with assert_raises(IOError):
+        with pytest.raises(IOError):
             super().test_replace_codes()
diff --git a/Allura/allura/tests/test_patches.py b/Allura/allura/tests/test_patches.py
index 0beb9ce96..ddaa0027a 100644
--- a/Allura/allura/tests/test_patches.py
+++ b/Allura/allura/tests/test_patches.py
@@ -17,10 +17,7 @@
 
 import webob
 from mock import patch
-from alluratest.tools import (
-    assert_equal,
-    assert_raises,
-)
+import pytest
 import tg
 
 from allura.lib import patches
@@ -33,7 +30,7 @@ def empty_func():
 @patch.object(patches, 'request', webob.Request.blank('/foo/bar'))
 def test_with_trailing_slash():
     patches.apply()
-    with assert_raises(webob.exc.HTTPMovedPermanently) as raised:
+    with pytest.raises(webob.exc.HTTPMovedPermanently) as raised:
         tg.decorators.with_trailing_slash(empty_func)()
     assert raised.exception.location == 'http://localhost/foo/bar/'
 
@@ -48,7 +45,7 @@ def test_with_trailing_slash_ok():
 @patch.object(patches, 'request', webob.Request.blank('/foo/bar?foo=bar&baz=bam'))
 def test_with_trailing_slash_qs():
     patches.apply()
-    with assert_raises(webob.exc.HTTPMovedPermanently) as raised:
+    with pytest.raises(webob.exc.HTTPMovedPermanently) as raised:
         tg.decorators.with_trailing_slash(empty_func)()
     assert raised.exception.location == 'http://localhost/foo/bar/?foo=bar&baz=bam'
 
@@ -56,7 +53,7 @@ def test_with_trailing_slash_qs():
 @patch.object(patches, 'request', webob.Request.blank('/foo/bar/'))
 def test_without_trailing_slash():
     patches.apply()
-    with assert_raises(webob.exc.HTTPMovedPermanently) as raised:
+    with pytest.raises(webob.exc.HTTPMovedPermanently) as raised:
         tg.decorators.without_trailing_slash(empty_func)()
     assert raised.exception.location == 'http://localhost/foo/bar'
 
@@ -71,6 +68,6 @@ def test_without_trailing_slash_ok():
 @patch.object(patches, 'request', webob.Request.blank('/foo/bar/?foo=bar&baz=bam'))
 def test_without_trailing_slash_qs():
     patches.apply()
-    with assert_raises(webob.exc.HTTPMovedPermanently) as raised:
+    with pytest.raises(webob.exc.HTTPMovedPermanently) as raised:
         tg.decorators.without_trailing_slash(empty_func)()
     assert raised.exception.location == 'http://localhost/foo/bar?foo=bar&baz=bam'
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index cfe441693..49206ba64 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -23,16 +23,8 @@ from tg import tmpl_context as c
 from webob import Request, exc
 from bson import ObjectId
 from ming.orm.ormsession import ThreadLocalORMSession
-from alluratest.tools import (
-    assert_equals,
-    assert_equal,
-    assert_raises,
-    assert_is_none,
-    assert_is,
-    assert_true,
-    assert_false,
-)
 from mock import Mock, MagicMock, patch
+import pytest
 
 from allura import model as M
 from allura.lib import plugin
@@ -78,16 +70,16 @@ class TestProjectRegistrationProvider:
         v = self.provider.shortname_validator.to_python
 
         v('thisislegit', neighborhood=nbhd)
-        assert_raises(ProjectShortnameInvalid, v,
+        pytest.raises(ProjectShortnameInvalid, v,
                       'not valid', neighborhood=nbhd)
-        assert_raises(ProjectShortnameInvalid, v,
+        pytest.raises(ProjectShortnameInvalid, v,
                       'this-is-valid-but-too-long', neighborhood=nbhd)
-        assert_raises(ProjectShortnameInvalid, v,
+        pytest.raises(ProjectShortnameInvalid, v,
                       'this is invalid and too long', neighborhood=nbhd)
-        assert_raises(ProjectShortnameInvalid, v,
+        pytest.raises(ProjectShortnameInvalid, v,
                       'end-dash-', neighborhood=nbhd)
         Project.query.get.return_value = Mock()
-        assert_raises(ProjectConflict, v, 'thisislegit', neighborhood=nbhd)
+        pytest.raises(ProjectConflict, v, 'thisislegit', neighborhood=nbhd)
 
 
 @with_nose_compatibility
@@ -652,7 +644,7 @@ class TestLocalAuthenticationProvider:
         user.__ming__ = Mock()
         self.provider.validate_password = lambda u, p: False
         self.provider._encode_password = Mock()
-        assert_raises(
+        pytest.raises(
             exc.HTTPUnauthorized,
             self.provider.set_password, user, 'old', 'new')
         assert self.provider._encode_password.call_count == 0
diff --git a/Allura/allura/tests/test_security.py b/Allura/allura/tests/test_security.py
index f6771f3aa..fea9bc54d 100644
--- a/Allura/allura/tests/test_security.py
+++ b/Allura/allura/tests/test_security.py
@@ -16,7 +16,7 @@
 #       under the License.
 
 from tg import tmpl_context as c
-from alluratest.tools import assert_equal, assert_raises
+import pytest
 
 from ming.odm import ThreadLocalODMSession
 from allura.tests import decorators as td
@@ -51,7 +51,7 @@ def _add_to_group(user, role):
 
 @patch('allura.lib.security.requests.get', side_effect=Timeout())
 def test_check_breached_password(r_get):
-    with assert_raises(HIBPClientError):
+    with pytest.raises(HIBPClientError):
         HIBPClient.check_breached_password('qwerty')
 
 
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index 4c22c1a93..5afc2c9b8 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -27,14 +27,9 @@ from ming.odm import session
 from bson import ObjectId
 from webob import Request
 from mock import Mock, patch
-from alluratest.tools import (
-    assert_equal,
-    assert_not_equal,
-    assert_raises,
-    assert_in,
-)
 from pygments import highlight
 from pygments.lexers import get_lexer_for_filename
+import pytest
 from tg import config
 import html5lib
 import html5lib.treewalkers
@@ -363,9 +358,9 @@ def test_empty_cursor():
     assert cursor.hint('index') == cursor
     assert cursor.extensions == []
     assert cursor.options(arg1='val1', arg2='val2') == cursor
-    assert_raises(ValueError, cursor.one)
-    assert_raises(StopIteration, cursor.next)
-    assert_raises(StopIteration, cursor._next_impl)
+    pytest.raises(ValueError, cursor.one)
+    pytest.raises(StopIteration, cursor.next)
+    pytest.raises(StopIteration, cursor._next_impl)
 
 
 def test_DateJSONEncoder():
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 3b11f5e05..c1eeaa0e8 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -21,12 +21,7 @@ import hashlib
 import datetime as dt
 
 from mock import Mock, MagicMock, patch, call
-from alluratest.tools import (
-    assert_raises,
-    assert_equal,
-    assert_not_in,
-    assert_in,
-)
+import pytest
 from datadiff import tools as dd
 from formencode import Invalid
 from ming.odm import session
@@ -100,10 +95,10 @@ class TestValidators(TestWebhookBase):
         app = self.git
         invalid_app = self.project.app_instance('src2')
         v = WebhookValidator(sender=sender, app=app, not_empty=True)
-        with assert_raises(Invalid) as cm:
+        with pytest.raises(Invalid) as cm:
             v.to_python(None)
         assert cm.exception.msg == 'Please enter a value'
-        with assert_raises(Invalid) as cm:
+        with pytest.raises(Invalid) as cm:
             v.to_python('invalid id')
         assert cm.exception.msg == 'Invalid webhook'
 
@@ -113,14 +108,14 @@ class TestValidators(TestWebhookBase):
                        secret='secret')
         session(wh).flush(wh)
         # invalid type
-        with assert_raises(Invalid) as cm:
+        with pytest.raises(Invalid) as cm:
             v.to_python(wh._id)
         assert cm.exception.msg == 'Invalid webhook'
 
         wh.type = 'repo-push'
         session(wh).flush(wh)
         # invalild app
-        with assert_raises(Invalid) as cm:
+        with pytest.raises(Invalid) as cm:
             v.to_python(wh._id)
         assert cm.exception.msg == 'Invalid webhook'
 
diff --git a/Allura/allura/tests/unit/spam/test_akismet.py b/Allura/allura/tests/unit/spam/test_akismet.py
index 2c6e2af31..384ce1b32 100644
--- a/Allura/allura/tests/unit/spam/test_akismet.py
+++ b/Allura/allura/tests/unit/spam/test_akismet.py
@@ -34,7 +34,7 @@ from alluratest.pytest_helpers import with_nose_compatibility
 class TestAkismet(unittest.TestCase):
 
     @mock.patch('allura.lib.spam.akismetfilter.akismet')
-    def setUp(self, akismet_lib):
+    def setup_method(self, method, akismet_lib):
         self.akismet = AkismetSpamFilter({'spam.key': 'example', 'base_url': 'http://localhost/'})
 
         def side_effect(*args, **kw):
diff --git a/Allura/allura/tests/unit/test_helpers/test_set_context.py b/Allura/allura/tests/unit/test_helpers/test_set_context.py
index 9e6999231..68b8898e4 100644
--- a/Allura/allura/tests/unit/test_helpers/test_set_context.py
+++ b/Allura/allura/tests/unit/test_helpers/test_set_context.py
@@ -15,7 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from alluratest.tools import assert_raises
+import pytest
 from tg import tmpl_context as c
 from bson import ObjectId
 
@@ -101,14 +101,14 @@ class TestWhenProjectIsNotFound(WithDatabase):
 
     def test_that_it_raises_an_exception(self):
         nbhd = create_neighborhood()
-        assert_raises(NoSuchProjectError,
+        pytest.raises(NoSuchProjectError,
                       set_context,
                       'myproject',
                       neighborhood=nbhd)
 
     def test_proper_exception_when_id_lookup(self):
         create_neighborhood()
-        assert_raises(NoSuchProjectError,
+        pytest.raises(NoSuchProjectError,
                       set_context,
                       ObjectId(),
                       neighborhood=None)
@@ -118,7 +118,7 @@ class TestWhenProjectIsNotFound(WithDatabase):
 class TestWhenNeighborhoodIsNotFound(WithDatabase):
 
     def test_that_it_raises_an_exception(self):
-        assert_raises(NoSuchNeighborhoodError,
+        pytest.raises(NoSuchNeighborhoodError,
                       set_context,
                       'myproject',
                       neighborhood='myneighborhood')
diff --git a/Allura/allura/tests/unit/test_package_path_loader.py b/Allura/allura/tests/unit/test_package_path_loader.py
index 5aec5fea2..5e3702715 100644
--- a/Allura/allura/tests/unit/test_package_path_loader.py
+++ b/Allura/allura/tests/unit/test_package_path_loader.py
@@ -20,8 +20,8 @@ from collections import OrderedDict
 from unittest import TestCase
 
 import jinja2
-from alluratest.tools import assert_equal, assert_raises
 import mock
+import pytest
 from tg import config
 
 from allura.lib.package_path_loader import PackagePathLoader
@@ -81,7 +81,7 @@ class TestPackagePathLoader(TestCase):
         for ep in eps:
             ep.name = ep.ep_name
             ep.load.return_value.template_path_rules = ep.rules
-        assert_raises(jinja2.TemplateError, PackagePathLoader()._load_rules)
+        pytest.raises(jinja2.TemplateError, PackagePathLoader()._load_rules)
 
     def test_replace_signposts(self):
         ppl = PackagePathLoader()
@@ -219,14 +219,14 @@ class TestPackagePathLoader(TestCase):
         ppl.init_paths = mock.Mock()
         fs_loader().get_source.side_effect = jinja2.TemplateNotFound('test')
 
-        assert_raises(
+        pytest.raises(
             jinja2.TemplateError,
             ppl.get_source, 'env', 'allura.ext.admin:templates/audit.html')
         assert fs_loader().get_source.call_count == 1
         fs_loader().get_source.reset_mock()
 
         with mock.patch.dict(config, {'disable_template_overrides': False}):
-            assert_raises(
+            pytest.raises(
                 jinja2.TemplateError,
                 ppl.get_source, 'env', 'allura.ext.admin:templates/audit.html')
             assert fs_loader().get_source.call_count == 2
diff --git a/ForgeActivity/forgeactivity/tests/functional/test_rest.py b/ForgeActivity/forgeactivity/tests/functional/test_rest.py
index d20166d5f..5ee7b6b0b 100644
--- a/ForgeActivity/forgeactivity/tests/functional/test_rest.py
+++ b/ForgeActivity/forgeactivity/tests/functional/test_rest.py
@@ -23,13 +23,13 @@ from alluratest.controller import TestRestApiBase
 
 class TestActivityHasAccessAPI(TestRestApiBase):
 
-    def setUp(self, *args, **kwargs):
-        super().setUp(*args, **kwargs)
+    def setup_method(self, method, *args, **kwargs):
+        super().setup_method(method, *args, **kwargs)
         self._enabled = config.get('activitystream.enabled', 'false')
         config['activitystream.enabled'] = 'true'
 
-    def tearDown(self, *args, **kwargs):
-        super().tearDown(*args, **kwargs)
+    def teardown_method(self, method, *args, **kwargs):
+        super().teardown_method(method, *args, **kwargs)
         config['activitystream.enabled'] = self._enabled
 
     def test_has_access_no_params(self):
diff --git a/ForgeActivity/forgeactivity/tests/functional/test_root.py b/ForgeActivity/forgeactivity/tests/functional/test_root.py
index cb90360d1..565576e05 100644
--- a/ForgeActivity/forgeactivity/tests/functional/test_root.py
+++ b/ForgeActivity/forgeactivity/tests/functional/test_root.py
@@ -33,13 +33,13 @@ from allura.tests import decorators as td
 
 class TestActivityController(TestController):
 
-    def setUp(self, *args, **kwargs):
-        super().setUp(*args, **kwargs)
+    def setup_method(self, method, *args, **kwargs):
+        super().setup_method(method, *args, **kwargs)
         self._enabled = config.get('activitystream.enabled', 'false')
         config['activitystream.enabled'] = 'true'
 
-    def tearDown(self, *args, **kwargs):
-        super().tearDown(*args, **kwargs)
+    def teardown_method(self, method, *args, **kwargs):
+        super().teardown_method(method, *args, **kwargs)
         config['activitystream.enabled'] = self._enabled
 
     def test_index(self):
diff --git a/ForgeBlog/forgeblog/tests/functional/test_rest.py b/ForgeBlog/forgeblog/tests/functional/test_rest.py
index 9e8a87756..068a5e911 100644
--- a/ForgeBlog/forgeblog/tests/functional/test_rest.py
+++ b/ForgeBlog/forgeblog/tests/functional/test_rest.py
@@ -29,8 +29,8 @@ from forgeblog import model as BM
 
 class TestBlogApi(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_tool('test', 'Blog', 'blog')
diff --git a/ForgeBlog/forgeblog/tests/test_app.py b/ForgeBlog/forgeblog/tests/test_app.py
index bb224a057..c61b0a0e8 100644
--- a/ForgeBlog/forgeblog/tests/test_app.py
+++ b/ForgeBlog/forgeblog/tests/test_app.py
@@ -34,7 +34,7 @@ from forgeblog import model as BM
 
 class TestApp:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
 
     @td.with_tool('test', 'Blog', 'blog')
@@ -75,7 +75,7 @@ class TestApp:
 
 class TestBulkExport:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_global_objects()
 
diff --git a/ForgeBlog/forgeblog/tests/test_commands.py b/ForgeBlog/forgeblog/tests/test_commands.py
index b1aa45196..3b82d6801 100644
--- a/ForgeBlog/forgeblog/tests/test_commands.py
+++ b/ForgeBlog/forgeblog/tests/test_commands.py
@@ -34,7 +34,7 @@ test_config = pkg_resources.resource_filename(
     'allura', '../test.ini') + '#main'
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_global_objects()
 
diff --git a/ForgeBlog/forgeblog/tests/test_roles.py b/ForgeBlog/forgeblog/tests/test_roles.py
index 3fe6a64df..18403dce9 100644
--- a/ForgeBlog/forgeblog/tests/test_roles.py
+++ b/ForgeBlog/forgeblog/tests/test_roles.py
@@ -23,7 +23,7 @@ from allura.lib import security
 from allura.lib import helpers as h
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_global_objects()
     h.set_context('test', neighborhood='Projects')
diff --git a/ForgeBlog/forgeblog/tests/unit/__init__.py b/ForgeBlog/forgeblog/tests/unit/__init__.py
index 202091598..648c054e8 100644
--- a/ForgeBlog/forgeblog/tests/unit/__init__.py
+++ b/ForgeBlog/forgeblog/tests/unit/__init__.py
@@ -31,7 +31,7 @@ def setUp():
 
 class BlogTestWithModel:
 
-    def setUp(self):
+    def setup_method(self, method):
         bootstrap.wipe_database()
         project_reg = plugin.ProjectRegistrationProvider.get()
         c.user = bootstrap.create_user('Test User')
@@ -46,5 +46,5 @@ class BlogTestWithModel:
         ThreadLocalORMSession.flush_all()
         h.set_context('test', 'blog', neighborhood='Projects')
 
-    def tearDown(self):
+    def teardown_method(self, method):
         ThreadLocalORMSession.close_all()
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index a20cb16e9..cfc2f5c84 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -16,6 +16,7 @@
 #       under the License.
 
 import mock
+import pytest
 import random
 import logging
 from six.moves.email_mime_text import MIMEText
@@ -30,7 +31,6 @@ from ming.odm import ThreadLocalORMSession
 from tg import tmpl_context as c
 from tg import config
 
-from alluratest.tools import assert_equal, assert_in, assert_not_in, assert_true, assert_false, assert_raises
 import feedparser
 
 from allura import model as M
@@ -45,8 +45,8 @@ log = logging.getLogger(__name__)
 
 
 class TestForumEmail(TestController):
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         c.user = M.User.by_username('test-admin')
         self.app.get('/discussion/')
         r = self.app.get('/admin/discussion/forums')
@@ -136,8 +136,8 @@ class TestForumMessageHandling(TestController):
     Tests all the "handle_message" related logic, which is what inbound emails run through
     '''
 
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         self.app.get('/discussion/')
         r = self.app.get('/admin/discussion/forums')
         form = r.forms['add-forum']
@@ -302,8 +302,8 @@ class TestForumMessageHandling(TestController):
 
 
 class TestForum(TestController):
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         self.app.get('/discussion/')
         r = self.app.get('/admin/discussion/forums')
         form = r.forms['add-forum']
@@ -496,7 +496,7 @@ class TestForum(TestController):
             self.test_posting()
 
             # second should fail
-            with assert_raises(Exception):
+            with pytest.raises(Exception):
                 self.test_posting()
 
     def test_notifications_escaping(self):
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
index 19babaca5..22fea58a3 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
@@ -29,8 +29,8 @@ log = logging.getLogger(__name__)
 
 class TestForumAdmin(TestController):
 
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         self.app.get('/discussion/')
 
     def test_forum_CRUD(self):
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index ff7449d1e..1219d2a87 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -31,8 +31,8 @@ from alluratest.controller import TestRestApiBase
 
 class TestImportController(TestRestApiBase):  # TestController):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         here_dir = os.path.dirname(__file__)
         self.app.get('/discussion/')
         self.json_text = open(here_dir + '/data/sf.json').read()
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
index b20d610a2..cebce6467 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
@@ -15,8 +15,6 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from alluratest.tools import assert_equal, assert_in
-
 from allura.lib import helpers as h
 from allura.tests import decorators as td
 from allura import model as M
@@ -27,8 +25,8 @@ from ming.orm import ThreadLocalORMSession
 
 class TestDiscussionApiBase(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_discussion
diff --git a/ForgeDiscussion/forgediscussion/tests/test_app.py b/ForgeDiscussion/forgediscussion/tests/test_app.py
index bb0b8d64b..0bf49aeca 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_app.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_app.py
@@ -53,7 +53,7 @@ class TestApp(TestDiscussionApiBase):  # creates some sample data
 
 class TestAppSitemap:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_unit_test()
         self.user = M.User.query.get(username='root')
diff --git a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
index 4bc0eef66..f05c6d993 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
@@ -23,7 +23,7 @@ from allura.lib import security
 from allura.tests import decorators as td
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_global_objects()
 
diff --git a/ForgeFeedback/forgefeedback/tests/functional/test_root.py b/ForgeFeedback/forgefeedback/tests/functional/test_root.py
index 37866f797..69243f0d5 100644
--- a/ForgeFeedback/forgefeedback/tests/functional/test_root.py
+++ b/ForgeFeedback/forgefeedback/tests/functional/test_root.py
@@ -17,9 +17,6 @@
 from tg import tmpl_context as c
 from tg import config
 
-from alluratest.tools import assert_equal, assert_in, assert_not_in
-from alluratest.tools import assert_true, assert_false, assert_raises
-
 from allura import model as M
 from alluratest.controller import TestController
 from allura.lib import helpers as h
@@ -30,9 +27,6 @@ from forgefeedback import model as FM
 
 class TestFeedback(TestController):
 
-    def setUp(self):
-        TestController.setUp(self)
-
     def test_feedback(self):
         c.user = M.User.by_username('test-admin')
         self.app.get('/feedback/')
diff --git a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
index 39ba76c70..64b626290 100644
--- a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
+++ b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
@@ -26,7 +26,7 @@ from allura.tests import decorators as td
 from allura.lib import helpers as h
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_with_tools()
 
diff --git a/ForgeFeedback/forgefeedback/tests/unit/__init__.py b/ForgeFeedback/forgefeedback/tests/unit/__init__.py
index b658fcab8..1bd8052c5 100644
--- a/ForgeFeedback/forgefeedback/tests/unit/__init__.py
+++ b/ForgeFeedback/forgefeedback/tests/unit/__init__.py
@@ -31,7 +31,7 @@ def setUp():
 
 class FeedbackTestWithModel:
 
-    def setUp(self):
+    def setup_method(self, method):
         bootstrap.wipe_database()
         project_reg = plugin.ProjectRegistrationProvider.get()
         c.user = bootstrap.create_user('Test User')
@@ -46,5 +46,5 @@ class FeedbackTestWithModel:
         ThreadLocalORMSession.flush_all()
         h.set_context('test', 'feedback', neighborhood='Projects')
 
-    def tearDown(self):
+    def teardown_method(self, method):
         ThreadLocalORMSession.close_all()
diff --git a/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py b/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
index d636594a8..f6b2b0ca8 100644
--- a/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
+++ b/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
@@ -33,8 +33,8 @@ from forgefeedback import feedback_main
 
 class TestFeedbackApp(FeedbackTestWithModel):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         c.user = User(username='test-user')
         h.set_context('test', 'feedback', neighborhood='Projects')
 
diff --git a/ForgeFiles/forgefiles/tests/functional/test_root.py b/ForgeFiles/forgefiles/tests/functional/test_root.py
index 999fd5367..5c44760f1 100644
--- a/ForgeFiles/forgefiles/tests/functional/test_root.py
+++ b/ForgeFiles/forgefiles/tests/functional/test_root.py
@@ -28,9 +28,6 @@ from testfixtures import TempDirectory
 
 
 class TestFiles(TestController):
-    def setUp(self):
-        TestController.setUp(self)
-
     def test_files(self):
         c.user = M.User.by_username('test-admin')
         r = self.app.get('/files/')
diff --git a/ForgeFiles/forgefiles/tests/model/__init__.py b/ForgeFiles/forgefiles/tests/model/__init__.py
index 9def3d957..72fc7a79b 100644
--- a/ForgeFiles/forgefiles/tests/model/__init__.py
+++ b/ForgeFiles/forgefiles/tests/model/__init__.py
@@ -31,7 +31,7 @@ def setUp():
 
 class FilesTestWithModel:
 
-    def setUp(self):
+    def setup_method(self, method):
         bootstrap.wipe_database()
         project_reg = plugin.ProjectRegistrationProvider.get()
         c.user = bootstrap.create_user('Test User')
@@ -46,6 +46,6 @@ class FilesTestWithModel:
         ThreadLocalORMSession.flush_all()
         h.set_context('test', 'files', neighborhood='Projects')
 
-    def tearDown(self):
+    def teardown_method(self, method):
         ThreadLocalORMSession.close_all()
 
diff --git a/ForgeFiles/forgefiles/tests/test_files_roles.py b/ForgeFiles/forgefiles/tests/test_files_roles.py
index 2675a3e51..6df1ae701 100644
--- a/ForgeFiles/forgefiles/tests/test_files_roles.py
+++ b/ForgeFiles/forgefiles/tests/test_files_roles.py
@@ -25,7 +25,7 @@ from allura.tests import decorators as td
 from allura.lib import helpers as h
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_with_tools()
 
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index cabeb2b0c..c681d5bbd 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -44,8 +44,8 @@ import six
 
 
 class _TestCase(TestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -78,8 +78,8 @@ class _TestCase(TestController):
 
 
 class TestUIController(TestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -570,8 +570,8 @@ class TestRestController(_TestCase):
 
 
 class TestHasAccessAPI(TestRestApiBase):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -620,8 +620,8 @@ class TestHasAccessAPI(TestRestApiBase):
 
 
 class TestFork(_TestCase):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         to_project = M.Project.query.get(
             shortname='test2', neighborhood_id=c.project.neighborhood_id)
         r = self.app.post('/src-git/fork', params=dict(
@@ -969,8 +969,8 @@ class TestFork(_TestCase):
 
 
 class TestDiff(TestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -998,8 +998,8 @@ class TestDiff(TestController):
 
 
 class TestGitRename(TestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -1066,8 +1066,8 @@ class TestGitRename(TestController):
 
 
 class TestGitBranch(TestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_git
@@ -1121,8 +1121,8 @@ class TestGitBranch(TestController):
 
 
 class TestIncludeMacro(_TestCase):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         setup_global_objects()
 
     def test_parse_repo(self):
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index bb687b91b..7d29d7451 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -48,7 +48,7 @@ from forgewiki import model as WM
 
 class TestNewGit(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -126,7 +126,7 @@ class TestNewGit(unittest.TestCase):
 
 class TestGitRepo(unittest.TestCase, RepoImplTestBase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -373,7 +373,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         self.assertEqual(new_tree.other_ids, orig_tree.other_ids)
 
     def test_refresh(self):
-        # test results of things that ran during setUp
+        # test results of things that ran during setup_method
         notification = M.Notification.query.find({'subject': '[test:src-git] 5 new commits to Git'}).first()
         assert notification
         domain = '.'.join(reversed(c.app.url[1:-1].split('/'))).replace('_', '-')
@@ -1026,7 +1026,7 @@ class TestGitImplementation(unittest.TestCase):
 
 class TestGitCommit(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -1102,7 +1102,7 @@ class TestGitCommit(unittest.TestCase):
 
 class TestGitHtmlView(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -1133,7 +1133,7 @@ class TestGitHtmlView(unittest.TestCase):
 
 class TestGitRename(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
diff --git a/ForgeGit/forgegit/tests/test_git_app.py b/ForgeGit/forgegit/tests/test_git_app.py
index 823004c40..c4868448a 100644
--- a/ForgeGit/forgegit/tests/test_git_app.py
+++ b/ForgeGit/forgegit/tests/test_git_app.py
@@ -28,7 +28,7 @@ from forgegit.tests import with_git
 
 class TestGitApp(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
diff --git a/ForgeGit/forgegit/tests/test_tasks.py b/ForgeGit/forgegit/tests/test_tasks.py
index 7a8921435..26e22c644 100644
--- a/ForgeGit/forgegit/tests/test_tasks.py
+++ b/ForgeGit/forgegit/tests/test_tasks.py
@@ -34,7 +34,7 @@ from forgegit.tests.functional.test_controllers import _TestCase as GitRealDataB
 
 class TestGitTasks(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -69,8 +69,8 @@ class TestCoreAlluraTasks(GitRealDataBaseTestCase):
     Not git-specific things we are testing, but the git tool is a useful standard repo type to use for it
     """
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     def test_refreshrepo(self):
diff --git a/ForgeImporters/forgeimporters/github/tests/test_code.py b/ForgeImporters/forgeimporters/github/tests/test_code.py
index d86d16031..d2ba20a20 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_code.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_code.py
@@ -35,7 +35,7 @@ with_git = with_tool(test_project_with_repo, 'git', 'src', 'git')
 
 class TestGitHubRepoImporter(TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_unit_test()
 
     def _make_project(self, gh_proj_name=None):
diff --git a/ForgeImporters/forgeimporters/github/tests/test_oauth.py b/ForgeImporters/forgeimporters/github/tests/test_oauth.py
index 1e02d83e0..c8bbadf99 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_oauth.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_oauth.py
@@ -27,8 +27,8 @@ from forgeimporters.github import GitHubOAuthMixin
 
 class TestGitHubOAuthMixin(TestController, TestCase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         setup_unit_test()
         c.user = Mock()
         self.mix = GitHubOAuthMixin()
diff --git a/ForgeImporters/forgeimporters/github/tests/test_utils.py b/ForgeImporters/forgeimporters/github/tests/test_utils.py
index 5e47bcefe..5f067e8bc 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_utils.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_utils.py
@@ -22,7 +22,7 @@ from forgeimporters.github.utils import GitHubMarkdownConverter
 
 class TestGitHubMarkdownConverter:
 
-    def setUp(self):
+    def setup_method(self, method):
         self.conv = GitHubMarkdownConverter('user', 'project')
 
     def test_convert_sha(self):
diff --git a/ForgeImporters/forgeimporters/github/tests/test_wiki.py b/ForgeImporters/forgeimporters/github/tests/test_wiki.py
index 5afcebb17..20f01002b 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_wiki.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_wiki.py
@@ -74,7 +74,7 @@ class TestGitHubWikiImporter(TestCase):
                 project=p, user=u, url='foo')
             g.post_event.assert_called_once_with('project_updated')
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.blob1 = Mock()
         self.blob1.name = 'Home.md'
diff --git a/ForgeImporters/forgeimporters/tests/forge/test_discussion.py b/ForgeImporters/forgeimporters/tests/forge/test_discussion.py
index 5fd08d631..01788aab1 100644
--- a/ForgeImporters/forgeimporters/tests/forge/test_discussion.py
+++ b/ForgeImporters/forgeimporters/tests/forge/test_discussion.py
@@ -33,14 +33,11 @@ from forgediscussion import utils
 
 class TestDiscussionImporter(TestCase):
 
-    def setUp(self):
-        super().setUp()
-
+    def setup_method(self, method):
         self.patcher_g = mock.patch('forgeimporters.base.g', mock.MagicMock())
         self.patcher_g.start()
 
-    def tearDown(self):
-        super().tearDown()
+    def teardown_method(self, method):
         self.patcher_g.stop()
 
     @mock.patch.object(discussion, 'c')
@@ -1273,8 +1270,8 @@ class TestDiscussionImporter(TestCase):
 
 class TestForgeDiscussionController(TestController, TestCase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
 
     @with_discussion
     def test_index(self):
diff --git a/ForgeImporters/forgeimporters/tests/forge/test_tracker.py b/ForgeImporters/forgeimporters/tests/forge/test_tracker.py
index 9e8277099..f1c5b1aca 100644
--- a/ForgeImporters/forgeimporters/tests/forge/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/forge/test_tracker.py
@@ -33,14 +33,12 @@ from forgeimporters.forge import alluraImporter
 
 class TestTrackerImporter(TestCase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
         # every single test method here creates an importer and ToolImporterMeta uses 'g'
         self.patcher_g = mock.patch('forgeimporters.base.g', mock.MagicMock())
         self.patcher_g.start()
 
-    def tearDown(self):
-        super().tearDown()
+    def teardown_method(self, method):
         self.patcher_g.stop()
 
     @mock.patch.object(tracker, 'File')
@@ -338,9 +336,9 @@ class TestTrackerImporter(TestCase):
 
 class TestForgeTrackerImportController(TestController, TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         """Mount Allura importer on the Tracker admin controller"""
-        super().setUp()
+        super().setup_method(method)
         from forgetracker.tracker_main import TrackerAdminController
         TrackerAdminController._importer = \
                 tracker.ForgeTrackerImportController(tracker.ForgeTrackerImporter())
diff --git a/ForgeImporters/forgeimporters/tests/github/functional/test_github.py b/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
index 78dd617bf..7945bb209 100644
--- a/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
+++ b/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
@@ -47,8 +47,8 @@ class TestGitHubImportController(TestController, TestCase):
 
 class TestGitHubOAuth(TestController):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         tg.config['github_importer.client_id'] = 'client_id'
         tg.config['github_importer.client_secret'] = 'secret'
 
diff --git a/ForgeImporters/forgeimporters/tests/github/test_extractor.py b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
index 0d890c6e4..3edac5eb3 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
@@ -84,7 +84,7 @@ class TestGitHubProjectExtractor(TestCase):
         response.info = lambda: headers
         return response
 
-    def setUp(self):
+    def setup_method(self, method):
         self.extractor = github.GitHubProjectExtractor('test_project')
         self.extractor.urlopen = self.mocked_urlopen
 
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
index 2219295cf..e06fcb3e1 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -27,14 +27,12 @@ from forgeimporters.github.utils import GitHubMarkdownConverter
 
 class TestTrackerImporter(TestCase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
         # every single test method here creates an importer and ToolImporterMeta uses 'g'
         self.patcher_g = mock.patch('forgeimporters.base.g', mock.MagicMock())
         self.patcher_g.start()
 
-    def tearDown(self):
-        super().tearDown()
+    def teardown_method(self, method):
         self.patcher_g.stop()
 
     @mock.patch.object(tracker, 'g')
diff --git a/ForgeImporters/forgeimporters/tests/test_base.py b/ForgeImporters/forgeimporters/tests/test_base.py
index b5d6dbe74..fe3d18d36 100644
--- a/ForgeImporters/forgeimporters/tests/test_base.py
+++ b/ForgeImporters/forgeimporters/tests/test_base.py
@@ -20,8 +20,8 @@ import errno
 
 from formencode import Invalid
 import mock
+import pytest
 from tg import expose, config
-from alluratest.tools import assert_equal, assert_raises
 from webob.exc import HTTPUnauthorized
 
 from alluratest.controller import TestController, setup_basic_test
@@ -89,7 +89,7 @@ def test_import_tool_failed(g, ToolImporter, format_exc):
     importer.import_tool.side_effect = RuntimeError('my error')
     ToolImporter.return_value = importer
 
-    with assert_raises(RuntimeError):
+    with pytest.raises(RuntimeError):
         base.import_tool('forgeimporters.base.ToolImporter', project_name='project_name')
     g.post_event.assert_called_once_with(
         'import_tool_task_failed',
@@ -248,7 +248,7 @@ class TestToolImporter(TestCase):
 
 class TestToolsValidator(TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         self.tv = base.ToolsValidator('good-source')
 
     @mock.patch.object(base.ToolImporter, 'by_name')
@@ -372,8 +372,8 @@ def test_save_importer_upload(giup, os):
         fp.write.assert_called_once_with('data')
 
     os.makedirs.side_effect = OSError(errno.EACCES, 'foo')
-    assert_raises(OSError, base.save_importer_upload,
-                  'project', 'file', 'data')
+    with pytest.raises(OSError):
+        base.save_importer_upload('project', 'file', 'data')
 
 
 class TestFile:
diff --git a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
index 07a524efc..1789654d7 100644
--- a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
@@ -40,7 +40,7 @@ from forgeimporters.trac.tickets import (
 
 class TestTracTicketImporter(TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_unit_test()
 
     @patch('forgeimporters.trac.tickets.session')
@@ -110,9 +110,9 @@ class TestTracTicketImporter(TestCase):
 
 class TestTracTicketImportController(TestController, TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         """Mount Trac import controller on the Tracker admin controller"""
-        super().setUp()
+        super().setup_method(method)
         from forgetracker.tracker_main import TrackerAdminController
         self.importer = TrackerAdminController._importer = TracTicketImportController(TracTicketImporter())
 
diff --git a/ForgeLink/forgelink/tests/functional/test_rest.py b/ForgeLink/forgelink/tests/functional/test_rest.py
index d420d537a..6b3e026d9 100644
--- a/ForgeLink/forgelink/tests/functional/test_rest.py
+++ b/ForgeLink/forgelink/tests/functional/test_rest.py
@@ -24,8 +24,8 @@ from allura.lib import helpers as h
 
 class TestLinkApi(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_link
@@ -80,8 +80,8 @@ class TestLinkApi(TestRestApiBase):
 
 class TestLinkHasAccess(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_link
diff --git a/ForgeLink/forgelink/tests/test_app.py b/ForgeLink/forgelink/tests/test_app.py
index 28efa0054..d6e2fb4a6 100644
--- a/ForgeLink/forgelink/tests/test_app.py
+++ b/ForgeLink/forgelink/tests/test_app.py
@@ -28,7 +28,7 @@ from alluratest.controller import setup_basic_test
 
 class TestBulkExport:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
 
     @td.with_link
diff --git a/ForgeSVN/forgesvn/tests/functional/test_controllers.py b/ForgeSVN/forgesvn/tests/functional/test_controllers.py
index 1a4cbe637..27b58e047 100644
--- a/ForgeSVN/forgesvn/tests/functional/test_controllers.py
+++ b/ForgeSVN/forgesvn/tests/functional/test_controllers.py
@@ -38,8 +38,8 @@ from allura.tests.decorators import with_tool
 
 class SVNTestController(TestController):
 
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     def _make_app(self, mount_point, name):
@@ -351,8 +351,8 @@ class TestImportController(SVNTestController):
 
 class SVNTestRenames(TestController):
 
-    def setUp(self):
-        TestController.setUp(self)
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @with_svn
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 9d5e02856..885aaaff1 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -53,7 +53,7 @@ import six
 
 class TestNewRepo(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -107,7 +107,7 @@ class TestNewRepo(unittest.TestCase):
 
 class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -573,7 +573,7 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
 
 class TestSVNRev(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -687,7 +687,7 @@ class _Test(unittest.TestCase):
     def _make_log(self, ci):
         session(ci).flush(ci)
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_global_objects()
         ThreadLocalORMSession.flush_all()
@@ -697,8 +697,8 @@ class _Test(unittest.TestCase):
 
 class _TestWithRepo(_Test):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         h.set_context('test', neighborhood='Projects')
         c.project.install_app('svn', 'test1')
         h.set_context('test', 'test1', neighborhood='Projects')
@@ -716,8 +716,8 @@ class _TestWithRepo(_Test):
 
 class _TestWithRepoAndCommit(_TestWithRepo):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.ci, isnew = self._make_commit('foo')
         ThreadLocalORMSession.flush_all()
         # ThreadLocalORMSession.close_all()
@@ -880,8 +880,8 @@ class TestRepoObject(_TestWithRepoAndCommit):
 
 class TestCommit(_TestWithRepo):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.ci, isnew = self._make_commit(
             'foo',
             a=dict(
@@ -1022,7 +1022,7 @@ class TestCommit(_TestWithRepo):
 
 class TestRename(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
@@ -1058,7 +1058,7 @@ class TestRename(unittest.TestCase):
 
 class TestDirectRepoAccess:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
diff --git a/ForgeSVN/forgesvn/tests/model/test_svnimplementation.py b/ForgeSVN/forgesvn/tests/model/test_svnimplementation.py
index 0667d1fdc..0313e822b 100644
--- a/ForgeSVN/forgesvn/tests/model/test_svnimplementation.py
+++ b/ForgeSVN/forgesvn/tests/model/test_svnimplementation.py
@@ -26,7 +26,7 @@ from forgesvn.model.svn import SVNImplementation
 
 class TestSVNImplementation:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_unit_test()
 
     def test_compute_tree_new(self):
diff --git a/ForgeSVN/forgesvn/tests/test_svn_app.py b/ForgeSVN/forgesvn/tests/test_svn_app.py
index 605ffe59c..d5c8770c1 100644
--- a/ForgeSVN/forgesvn/tests/test_svn_app.py
+++ b/ForgeSVN/forgesvn/tests/test_svn_app.py
@@ -28,7 +28,7 @@ from forgesvn.tests import with_svn
 
 class TestSVNApp(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
 
diff --git a/ForgeSVN/forgesvn/tests/test_tasks.py b/ForgeSVN/forgesvn/tests/test_tasks.py
index e95899ecf..b3e388e98 100644
--- a/ForgeSVN/forgesvn/tests/test_tasks.py
+++ b/ForgeSVN/forgesvn/tests/test_tasks.py
@@ -36,14 +36,14 @@ from forgesvn.tests import with_svn
 
 class TestRepoTasks(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         self.setup_with_tools()
         if asbool(tg.config.get('smtp.mock')):
             self.smtp_mock = mock.patch('allura.lib.mail_util.smtplib.SMTP')
             self.smtp_mock.start()
 
-    def tearDown(self):
+    def teardown_method(self, method):
         if asbool(tg.config.get('smtp.mock')):
             self.smtp_mock.stop()
 
diff --git a/ForgeShortUrl/forgeshorturl/tests/functional/test.py b/ForgeShortUrl/forgeshorturl/tests/functional/test.py
index 547ca8e01..1d3471d06 100644
--- a/ForgeShortUrl/forgeshorturl/tests/functional/test.py
+++ b/ForgeShortUrl/forgeshorturl/tests/functional/test.py
@@ -29,8 +29,8 @@ from forgeshorturl.model import ShortUrl
 
 class TestRootController(TestController):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_url
diff --git a/ForgeTracker/forgetracker/tests/command/test_fix_discussion.py b/ForgeTracker/forgetracker/tests/command/test_fix_discussion.py
index 9074e46ff..cba1fc793 100644
--- a/ForgeTracker/forgetracker/tests/command/test_fix_discussion.py
+++ b/ForgeTracker/forgetracker/tests/command/test_fix_discussion.py
@@ -30,7 +30,7 @@ test_config = pkg_resources.resource_filename(
     'allura', '../test.ini') + '#main'
 
 
-def setUp(self):
+def setup_method(self, method):
     """Method called by nose before running each test"""
     setup_basic_test()
     setup_global_objects()
diff --git a/ForgeTracker/forgetracker/tests/functional/test_rest.py b/ForgeTracker/forgetracker/tests/functional/test_rest.py
index be72dd250..7db63dfa2 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_rest.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_rest.py
@@ -32,8 +32,8 @@ from forgetracker import model as TM
 
 class TestTrackerApiBase(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_tool('test', 'Tickets', 'bugs',
@@ -101,8 +101,8 @@ class TestRestNewTicket(TestTrackerApiBase):
 
 class TestRestUpdateTicket(TestTrackerApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         ticket_view = self.create_ticket()
         self.ticket_args = ticket_view.json['ticket']
 
@@ -124,8 +124,8 @@ class TestRestUpdateTicket(TestTrackerApiBase):
 
 class TestRestIndex(TestTrackerApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.create_ticket()
 
     def test_ticket_index(self):
@@ -169,8 +169,8 @@ class TestRestIndex(TestTrackerApiBase):
 
 class TestRestDiscussion(TestTrackerApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         ticket_view = self.create_ticket()
         self.ticket_args = ticket_view.json['ticket']
 
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index e0556322e..1b97a8b37 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -28,15 +28,6 @@ import mock
 import PIL
 from bs4 import BeautifulSoup
 from mock import patch
-from alluratest.tools import (
-    assert_true,
-    assert_false,
-    assert_equal,
-    assert_in,
-    assert_raises,
-    assert_not_in,
-    assert_not_equal,
-)
 from formencode.variabledecode import variable_encode
 from tg import tmpl_context as c
 from tg import app_globals as g
@@ -2753,9 +2744,10 @@ def post_install_hook(app):
 
 
 class TestEmailMonitoring(TrackerTestController):
-    def __init__(self):
-        super().__init__()
-        self.test_email = 'mailinglist@example.com'
+
+    @classmethod
+    def setup_class(cls):
+        cls.test_email = 'mailinglist@example.com'
 
     def _set_options(self, monitoring_type='AllTicketChanges'):
         r = self.app.post('/admin/bugs/set_options', params={
@@ -2887,8 +2879,8 @@ class TestEmailMonitoring(TrackerTestController):
 
 
 class TestCustomUserField(TrackerTestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         params = dict(
             custom_fields=[
                 dict(name='_code_review', label='Code Review', type='user',
@@ -3018,8 +3010,8 @@ class TestShowDefaultFields(TrackerTestController):
 
 
 class TestBulkMove(TrackerTestController):
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.new_ticket(summary='A New Hope')
         self.new_ticket(summary='The Empire Strikes Back')
         self.new_ticket(summary='Return Of The Jedi')
diff --git a/ForgeTracker/forgetracker/tests/test_app.py b/ForgeTracker/forgetracker/tests/test_app.py
index aed0fb60b..a688e8d01 100644
--- a/ForgeTracker/forgetracker/tests/test_app.py
+++ b/ForgeTracker/forgetracker/tests/test_app.py
@@ -38,7 +38,7 @@ from alluratest.pytest_helpers import with_nose_compatibility
 
 class TestApp:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
 
     @td.with_tracker
diff --git a/ForgeTracker/forgetracker/tests/test_tracker_roles.py b/ForgeTracker/forgetracker/tests/test_tracker_roles.py
index 242646c8f..086e2796f 100644
--- a/ForgeTracker/forgetracker/tests/test_tracker_roles.py
+++ b/ForgeTracker/forgetracker/tests/test_tracker_roles.py
@@ -23,7 +23,7 @@ from allura.lib import security
 from allura.tests import decorators as td
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_with_tools()
 
diff --git a/ForgeTracker/forgetracker/tests/unit/__init__.py b/ForgeTracker/forgetracker/tests/unit/__init__.py
index 1e9a0a9d4..3783fc20f 100644
--- a/ForgeTracker/forgetracker/tests/unit/__init__.py
+++ b/ForgeTracker/forgetracker/tests/unit/__init__.py
@@ -33,7 +33,7 @@ def setUp():
 
 class TrackerTestWithModel:
 
-    def setUp(self):
+    def setup_method(self, method):
         bootstrap.wipe_database()
         project_reg = plugin.ProjectRegistrationProvider.get()
         c.user = bootstrap.create_user('Test User')
@@ -49,5 +49,5 @@ class TrackerTestWithModel:
         h.set_context('test', 'bugs', neighborhood='Projects')
         tg.request_local.context.request = Request.blank('/')
 
-    def tearDown(self):
+    def teardown_method(self, method):
         ThreadLocalORMSession.close_all()
diff --git a/ForgeTracker/forgetracker/tests/unit/test_globals_model.py b/ForgeTracker/forgetracker/tests/unit/test_globals_model.py
index 6c063c8f8..bb22df47a 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_globals_model.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_globals_model.py
@@ -30,8 +30,8 @@ from allura.lib import helpers as h
 
 class TestGlobalsModel(TrackerTestWithModel):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         c.project.install_app('Tickets', 'doc-bugs')
         ThreadLocalORMSession.flush_all()
 
diff --git a/ForgeTracker/forgetracker/tests/unit/test_root_controller.py b/ForgeTracker/forgetracker/tests/unit/test_root_controller.py
index 8051cde32..fd40593bc 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_root_controller.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_root_controller.py
@@ -32,16 +32,16 @@ from forgetracker import tracker_main
 
 class WithUserAndBugsApp(TrackerTestWithModel):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         c.user = User(username='test-user')
         h.set_context('test', 'bugs', neighborhood='Projects')
 
 
 class TestWhenSearchingWithCustomFields(WithUserAndBugsApp):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         with solr_search_returning_colors_are_wrong_ticket():
             self.response = tracker_main.RootController().search(q='friends')
 
@@ -57,8 +57,8 @@ class TestWhenSearchingWithCustomFields(WithUserAndBugsApp):
 
 class TestWhenLoadingFrontPage(WithUserAndBugsApp):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         with mongo_search_returning_colors_are_wrong_ticket():
             self.response = tracker_main.RootController().index()
 
diff --git a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
index 02744756b..940525363 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
@@ -22,16 +22,10 @@ import six.moves.urllib.request
 import six.moves.urllib.error
 
 import mock
+import pytest
 from ming.orm.ormsession import ThreadLocalORMSession
 from ming.orm import session
 from ming import schema
-from alluratest.tools import (
-    raises,
-    assert_equal,
-    assert_in,
-    assert_true,
-    assert_false,
-)
 from forgetracker.model import Ticket, TicketAttachment
 from forgetracker.tests.unit import TrackerTestWithModel
 from forgetracker.import_support import ResettableStream
@@ -76,9 +70,9 @@ class TestTicketModel(TrackerTestWithModel):
         ticket = Ticket.query.get(summary='my ticket')
         assert ticket.custom_fields == dict(my_field='my value')
 
-    @raises(schema.Invalid)
     def test_ticket_num_required(self):
-        Ticket(summary='my ticket')
+        with pytest.raises(schema.Invalid):
+            Ticket(summary='my ticket')
 
     def test_ticket_num_required2(self):
         t = Ticket(summary='my ticket', ticket_num=12)
diff --git a/ForgeUserStats/forgeuserstats/tests/test_model.py b/ForgeUserStats/forgeuserstats/tests/test_model.py
index 0d7bcba56..1a7720a11 100644
--- a/ForgeUserStats/forgeuserstats/tests/test_model.py
+++ b/ForgeUserStats/forgeuserstats/tests/test_model.py
@@ -38,7 +38,7 @@ with_git = td.with_tool('test', 'Git', 'git-userstats-model', 'Git', type='git')
 
 class TestUserStats(unittest.TestCase):
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_global_objects()
         self.user = User.by_username('test-user-2')
diff --git a/ForgeUserStats/forgeuserstats/tests/test_stats.py b/ForgeUserStats/forgeuserstats/tests/test_stats.py
index b36112658..cf206201b 100644
--- a/ForgeUserStats/forgeuserstats/tests/test_stats.py
+++ b/ForgeUserStats/forgeuserstats/tests/test_stats.py
@@ -31,8 +31,8 @@ from forgetracker import model as TM
 
 class TestStats(TestController):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         p = M.Project.query.get(shortname='test')
         p.add_user(M.User.by_username('test-user'), ['Admin'])
 
@@ -185,8 +185,8 @@ class TestStats(TestController):
 
 class TestGitCommit(TestController, unittest.TestCase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         setup_basic_test()
 
         user = User.by_username('test-admin')
diff --git a/ForgeWiki/forgewiki/tests/functional/test_rest.py b/ForgeWiki/forgewiki/tests/functional/test_rest.py
index 694275746..4eb87d999 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_rest.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_rest.py
@@ -29,8 +29,8 @@ from forgewiki.model import Page
 
 class TestWikiApi(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_wiki
@@ -121,8 +121,8 @@ class TestWikiApi(TestRestApiBase):
 
 class TestWikiHasAccess(TestRestApiBase):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_wiki
diff --git a/ForgeWiki/forgewiki/tests/functional/test_root.py b/ForgeWiki/forgewiki/tests/functional/test_root.py
index 05e2b181e..927f17dc4 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_root.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_root.py
@@ -37,8 +37,8 @@ from unittest.mock import MagicMock
 
 class TestRootController(TestController):
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self, method):
+        super().setup_method(method)
         self.setup_with_tools()
 
     @td.with_wiki
diff --git a/ForgeWiki/forgewiki/tests/test_app.py b/ForgeWiki/forgewiki/tests/test_app.py
index 3dc723c68..58159df5e 100644
--- a/ForgeWiki/forgewiki/tests/test_app.py
+++ b/ForgeWiki/forgewiki/tests/test_app.py
@@ -34,7 +34,7 @@ from forgewiki import model as WM
 
 class TestBulkExport:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_global_objects()
         self.setup_with_tools()
@@ -126,7 +126,7 @@ class TestBulkExport:
 
 class TestApp:
 
-    def setUp(self):
+    def setup_method(self, method):
         setup_basic_test()
         setup_global_objects()
         self.setup_with_tools()
diff --git a/ForgeWiki/forgewiki/tests/test_wiki_roles.py b/ForgeWiki/forgewiki/tests/test_wiki_roles.py
index e03880976..f42f76618 100644
--- a/ForgeWiki/forgewiki/tests/test_wiki_roles.py
+++ b/ForgeWiki/forgewiki/tests/test_wiki_roles.py
@@ -25,7 +25,7 @@ from allura.lib import security
 from allura.tests import decorators as td
 
 
-def setUp():
+def setup_module(module):
     setup_basic_test()
     setup_with_tools()
 
diff --git a/scripts/perf/call_count.py b/scripts/perf/call_count.py
index 711a21daa..4c567b7df 100755
--- a/scripts/perf/call_count.py
+++ b/scripts/perf/call_count.py
@@ -70,7 +70,7 @@ def main(args):
                         debug_html=args.debug_html)
     print(json.dumps(counts))
     write_csv(counts, args.id, args.data_file)
-    test.tearDown()
+    test.teardown_method(method)
 
 
 def setup(test):
@@ -79,7 +79,7 @@ def setup(test):
                                   'stats.debug_line_length': 1000,
                                   }), \
             patch('timermiddleware.log.isEnabledFor', return_value=True):  # can't set this via logging configuration since setUp() will load a logging config and then start using it before we have a good place to tweak it
-        test.setUp()
+        test.setup_method(method)
 
     tmw_log = logging.getLogger('timermiddleware')
     tmw_log.disabled = 0  # gets disabled when .ini file is loaded; dumb.